From f695dba5067058c75897611f8a70001be0facd51 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 21:30:19 +0200 Subject: [PATCH 001/110] made fields private and final where possible --- .../WindStreamletsCanvasOverlay.java | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 4f09cb84ae1..b98c9a0e7d6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -25,14 +25,13 @@ import com.sap.sse.gwt.client.player.Timer; * */ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria { - /** The wind field that is to be displayed in the overlay */ - protected WindFieldDTO windFieldDTO; - protected WindFieldGenParamsDTO windParams = null; - protected SimulatorMap simulatorMap; + private WindFieldDTO windFieldDTO; + private final WindFieldGenParamsDTO windParams; + private final SimulatorMap simulatorMap; private boolean visible = false; - private Timer timer; + private final Timer timer; private Date endDate; private int nParticles; @@ -43,17 +42,12 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) { super(simulatorMap.getMap(), zIndex); - this.simulatorMap = simulatorMap; this.timer = timer; this.windParams = windParams; - this.nParticles = this.simulatorMap.getMainPanel().particles; - windFieldDTO = null; - getCanvas().getElement().setId("swarm-display"); - } public WindFieldDTO getWindFieldDTO() { @@ -293,12 +287,4 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti return false; } } - - public void setTimer(final Timer timer) { - this.timer = timer; - } - - public Timer getTimer() { - return timer; - } } From 896f704345fa781a73c0ebe453184b17d55f5607 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 21:46:24 +0200 Subject: [PATCH 002/110] formatted java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java --- .../sailing/gwt/ui/simulator/streamlets/Index.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java index 59b47daac3b..c0d1346e246 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java @@ -1,12 +1,11 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; public class Index { + public long x; + public long y; - public long x; - public long y; - - public Index(long x, long y) { - this.x = x; - this.y = y; - } + public Index(long x, long y) { + this.x = x; + this.y = y; + } } From f3aea6ee60e9935fc13bfd7fc9c052a9d297ab50 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 21:46:44 +0200 Subject: [PATCH 003/110] removed unused method extendWindDataJSON --- .../WindStreamletsCanvasOverlay.java | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index b98c9a0e7d6..7e129596897 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -1,7 +1,6 @@ package com.sap.sailing.gwt.ui.simulator; import java.util.Date; -import java.util.List; import java.util.logging.Logger; import com.google.gwt.core.client.JavaScriptObject; @@ -162,46 +161,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } } - public void extendWindDataJSON() { - List data = this.windFieldDTO.getMatrix(); - String jsonData = "data:["; - int p = 0; - int imax = this.windParams.getyRes() + 2 * this.windParams.getBorderY(); - int jmax = this.windParams.getxRes() + 2 * this.windParams.getBorderX(); - int steps = data.size() / (imax * jmax); - double maxWindSpeed = 0; - for (int s = 0; s < steps; s++) { - jsonData += "["; - for (int i = 0; i < imax; i++) { - jsonData += "["; - for (int j = 0; j < jmax; j++) { - SimulatorWindDTO wind = data.get(p); - p++; - if (wind.trueWindSpeedInKnots > maxWindSpeed) { - maxWindSpeed = wind.trueWindSpeedInKnots; - } - double y = wind.trueWindSpeedInKnots * Math.cos(wind.trueWindBearingDeg * Math.PI / 180.0); - double x = wind.trueWindSpeedInKnots * Math.sin(wind.trueWindBearingDeg * Math.PI / 180.0); - jsonData += x + "," + y; - if (j < (jmax - 1)) { - jsonData += ","; - } - } - if (i < (imax - 1)) { - jsonData += "],"; - } else { - jsonData += "]"; - } - } - if (s < (steps - 1)) { - jsonData += "],"; - } else { - jsonData += "]"; - } - } - this.windFieldDTO.windDataJSON += jsonData + "],maxLength:" + maxWindSpeed + "};"; - } - public void setEndDate(Date endDate) { this.endDate = endDate; } From 059c26aa9c3576f2735b722bd91f8eb5c77dfbbd Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 21:49:41 +0200 Subject: [PATCH 004/110] fixed formatting --- .../ui/simulator/streamlets/Neighbors.java | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java index 22bf7c67037..176cb6f25a1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java @@ -1,20 +1,19 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; public class Neighbors { + public int xBot; + public int xTop; + public double xMod; + public int yBot; + public int yTop; + public double yMod; - public int xBot; - public int xTop; - public double xMod; - public int yBot; - public int yTop; - public double yMod; - - public Neighbors(int xTop, int yTop, int xBot, int yBot, double xMod, double yMod) { - this.xTop = xTop; - this.yTop = yTop; - this.xBot = xBot; - this.yBot = yBot; - this.xMod = xMod; - this.yMod = yMod; - } + public Neighbors(int xTop, int yTop, int xBot, int yBot, double xMod, double yMod) { + this.xTop = xTop; + this.yTop = yTop; + this.xBot = xBot; + this.yBot = yBot; + this.xMod = xMod; + this.yMod = yMod; + } } From e1ca6db32228f4665b298b9fda41999a943a28db Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 21:56:58 +0200 Subject: [PATCH 005/110] removed unused JSNI stuff --- .../WindStreamletsCanvasOverlay.java | 97 ------------------- 1 file changed, 97 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 7e129596897..1b4c600ddfd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -1,15 +1,7 @@ package com.sap.sailing.gwt.ui.simulator; import java.util.Date; -import java.util.logging.Logger; -import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.json.client.JSONArray; -import com.google.gwt.json.client.JSONNumber; -import com.google.gwt.json.client.JSONObject; -import com.google.gwt.json.client.JSONString; -import com.google.gwt.maps.client.base.LatLng; -import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; @@ -36,8 +28,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private int nParticles; private Swarm swarm; - private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlay.class.getName()); - public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) { super(simulatorMap.getMap(), zIndex); @@ -57,93 +47,10 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti return this.windParams; } - public JavaScriptObject getJSONRandomWindData() { - - JSONObject jsonWindData = new JSONObject(); - - jsonWindData.put("timestamp", new JSONString("11:55 am on March 18, 2014")); - - LatLng boundsSW = LatLng.newInstance(53.854617, 8.159124); - LatLng boundsNE = LatLng.newInstance(55.263922, 11.413824); - - jsonWindData.put("x0", new JSONNumber(boundsSW.getLongitude())); - jsonWindData.put("y0", new JSONNumber(boundsSW.getLatitude())); - jsonWindData.put("x1", new JSONNumber(boundsNE.getLongitude())); - jsonWindData.put("y1", new JSONNumber(boundsNE.getLatitude())); - - int maxSteps = 1; - int gridWidth = 100; - int gridHeight = 100; - jsonWindData.put("gridWidth", new JSONNumber(gridWidth)); - jsonWindData.put("gridHeight", new JSONNumber(gridHeight)); - - JSONArray windField = new JSONArray(); - - for (int stp = 0; stp < maxSteps; stp++) { - JSONArray vectorField = new JSONArray(); - for (int idx = 0; idx < (gridWidth * gridHeight * 2); idx++) { - vectorField.set(idx, new JSONNumber((Math.random() - 0.5) * 10.0 + 3.0)); - // windField.set(idx, new JSONNumber(10.0)); - } - windField.set(stp, vectorField); - } - jsonWindData.put("field", windField); - - return jsonWindData.getJavaScriptObject(); - } - - public native boolean isSwarmDataExt() /*-{ - if ($wnd.swarmDataExt) { - return true; - } else { - return false; - } - }-*/; - - public native String getSwarmData() /*-{ - return JSON.stringify($wnd.swarmData); - }-*/; - - public native void setSwarmData(JavaScriptObject swarmData) /*-{ - $wnd.swarmData = swarmData; - }-*/; - - private native void getJSNIWind(WindStreamletsCanvasOverlay wsc) /*-{ - $wnd.getWindfromSimulator = function(idx) { - return wsc.@com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay::getWind(I)(idx); - }; - }-*/; - - private native JavaScriptObject getWindInst(double x, double y) /*-{ - return { - x : x, - y : y - }; - }-*/; - - public JavaScriptObject getWind(int idx) { - SimulatorWindDTO wind = this.windFieldDTO.getMatrix().get(idx); - double y = wind.trueWindSpeedInKnots * Math.cos(wind.trueWindBearingDeg * Math.PI / 180.0); - double x = wind.trueWindSpeedInKnots * Math.sin(wind.trueWindBearingDeg * Math.PI / 180.0); - return getWindInst(x, y); - } - - public native void setWindDataJSON(String jsonField) /*-{ - eval(jsonField); - }-*/; - public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex) { this(simulatorMap, zIndex, null, null); } - public native void setMapInstance(Object mapInstance) /*-{ - $wnd.swarmMap = mapInstance; - }-*/; - - public native void setCanvasProjectionInstance(Object instance) /*-{ - $wnd.swarmCanvasProjection = instance; - }-*/; - public void startStreamlets() { if (swarm == null) { this.swarm = new Swarm(this, map); @@ -227,10 +134,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti return this.swarm; } - private void clear() { - this.stopStreamlets(); - } - @Override public void timeChanged(final Date newDate, Date oldDate) { int step = (int) ((newDate.getTime() - this.windParams.getStartTime().getTime()) / this.windParams From dfafb059c902d57ec8586a54f9644e6c0cab8083 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 21:58:20 +0200 Subject: [PATCH 006/110] uncompilable interims commit, avoiding JSON serialization of wind field data, factoring bounds stuff, moving things to Swarm where VectorField shoulnd't have to know --- .../gwt/ui/server/SimulatorServiceImpl.java | 73 +++------------- .../sailing/gwt/ui/shared/WindFieldDTO.java | 13 ++- .../gwt/ui/simulator/streamlets/Index.java | 4 +- .../ui/simulator/streamlets/Neighbors.java | 12 +-- .../ui/simulator/streamlets/RectField.java | 34 +------- .../simulator/streamlets/SimulatorField.java | 84 ++++--------------- .../gwt/ui/simulator/streamlets/Swarm.java | 48 +++++++++-- .../ui/simulator/streamlets/VectorField.java | 12 +-- 8 files changed, 94 insertions(+), 186 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java index 2065925c1c8..08536b403b9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java @@ -42,6 +42,7 @@ import com.sap.sailing.gwt.ui.shared.SimulatorResultsDTO; import com.sap.sailing.gwt.ui.shared.SimulatorUISelectionDTO; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; +import com.sap.sailing.gwt.ui.shared.WindFieldDTO.WindData; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; import com.sap.sailing.gwt.ui.shared.WindLatticeDTO; import com.sap.sailing.gwt.ui.shared.WindLatticeGenParamsDTO; @@ -763,74 +764,20 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula //} else { if (params.isShowStreamlets2()) { - - // generate JSON-vectorfield - String jsonField = "$wnd.windFieldSim={"; - //this.rcStart = field.rcStart; - jsonField += "rcStart:{lat:" + params.getNorthWest().latDeg + ",lng:" + params.getNorthWest().lngDeg + "},"; - //this.rcEnd = field.rcEnd; - jsonField += "rcEnd:{lat:" + params.getSouthEast().latDeg + ",lng:" + params.getSouthEast().lngDeg + "},"; + WindData windData = new WindData(); + windData.rcStart = params.getNorthWest(); + windData.rcEnd = params.getSouthEast(); // SouthWest jsonField += "boundsSW:{lat:" + positionGrid[0][0].getLatDeg() + ",lng:" + positionGrid[0][0].getLngDeg() + "},"; // NorthEast jsonField += "boundsNE:{lat:" + positionGrid[params.getyRes()+2*params.getBorderY()-1][params.getxRes()+2*params.getBorderX()-1].getLatDeg() + ",lng:" + positionGrid[params.getyRes()+2*params.getBorderY()-1][params.getxRes()+2*params.getBorderX()-1].getLngDeg() + "},"; //this.resY = field.resY; - jsonField += "resY:" + params.getyRes() + ","; - //this.resX = field.resX; - jsonField += "resX:" + params.getxRes() + ","; - // border - jsonField += "borderY:" + params.getBorderY() + ","; - jsonField += "borderX:" + params.getBorderX() + ","; - //this.xScale = field.xScale; - jsonField += "xScale:1.5,"; - - /*String jsonField2 = "data:["; - DecimalFormat df = new DecimalFormat("#.00"); - double maxWindSpeed = 0; - - //List windMat = wfDTO.getMatrix(); - - if (positionGrid != null && positionGrid.length > 0) { - TimePoint t = startTime; - while (t.compareTo(endTime) <= 0) { - jsonField2 += "["; - for (int i = 0; i < positionGrid.length; ++i) { - jsonField2 += "["; - for (int j = 0; j < positionGrid[i].length; ++j) { - Wind localWind = wf.getWind(new TimedPositionWithSpeedImpl(t, positionGrid[i][j], null)); - if (localWind.getKnots() > maxWindSpeed) { - maxWindSpeed = localWind.getKnots(); - } - double windY = localWind.getKnots() * Math.cos(localWind.getBearing().getRadians()); - double windX = localWind.getKnots() * Math.sin(localWind.getBearing().getRadians()); - jsonField2 += df.format(windX) + "," + df.format(windY); - if (j<(params.getxRes()-1)) { - jsonField2 += ","; - } - } - if (i<(params.getyRes()-1)) { - jsonField2 += "],"; - } else { - jsonField2 += "]"; - } - } - t = new MillisecondsTimePoint(t.asMillis() + timeStep.asMillis()); - if (t.compareTo(endTime) <= 0) { - jsonField2 += "],"; - } else { - jsonField2 += "]"; - } - } - } - jsonField += "maxLength:" + maxWindSpeed + "," + jsonField2; - jsonField += "]"; - - // finalize JSON Field - jsonField += "};";*/ - - //LOGGER.info("JSON Field:\n" + jsonField); - windFieldDTO.windDataJSON = jsonField; - // windFieldDTO.setMatrix(null); + windData.resX = params.getxRes(); + windData.resY = params.getyRes(); + windData.borderX = params.getBorderX(); + windData.borderY = params.getBorderY(); + windData.xScale = 1.5; + windFieldDTO.windData = windData; } return windFieldDTO; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java index cfb81a1889e..a7e39689ac0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java @@ -3,13 +3,24 @@ package com.sap.sailing.gwt.ui.shared; import java.util.List; import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.domain.common.dto.PositionDTO; public class WindFieldDTO implements IsSerializable { public double curSpeed; public double curBearing; - public String windDataJSON; + public static class WindData { + public PositionDTO rcStart; + public PositionDTO rcEnd; + public int resX; + public int resY; + public int borderX; + public int borderY; + public double xScale; + } + + public WindData windData; private List matrix; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java index c0d1346e246..6d6af04bbfa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java @@ -1,8 +1,8 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; public class Index { - public long x; - public long y; + public final long x; + public final long y; public Index(long x, long y) { this.x = x; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java index 176cb6f25a1..8b1951d8592 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java @@ -1,12 +1,12 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; public class Neighbors { - public int xBot; - public int xTop; - public double xMod; - public int yBot; - public int yTop; - public double yMod; + public final int xBot; + public final int xTop; + public final double xMod; + public final int yBot; + public final int yTop; + public final double yMod; public Neighbors(int xTop, int yTop, int xBot, int yBot, double xMod, double yMod) { this.xTop = xTop; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index dd248cc7867..83765a2f47d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -15,9 +15,6 @@ public class RectField implements VectorField { private final double y0; private final double y1; - private Position visSW; - private Position visNE; - private final int w; private final int h; @@ -31,10 +28,6 @@ public class RectField implements VectorField { this.x1 = x1; this.y0 = y0; this.y1 = y1; - - this.visSW = new DegreePosition(0.0, 0.0); - this.visNE = new DegreePosition(0.0, 0.0); - this.field = field; this.w = field.length; this.h = field[0].length; @@ -99,16 +92,7 @@ public class RectField implements VectorField { } @Override - public Position getRandomPosition() { - double rndY = Math.random(); - double rndX = Math.random(); - double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); - double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); - return new DegreePosition(latDeg, lngDeg); - } - - @Override - public boolean inBounds(Position p) { + public boolean inBounds(Position p, boolean visFull) { return p.getLngDeg() >= this.x0 && p.getLngDeg() < this.x1 && p.getLatDeg() >= this.y0 && p.getLatDeg() < this.y1; } @@ -185,27 +169,13 @@ public class RectField implements VectorField { } @Override - public Position[] getFieldCorners() { + public Position[] getFieldCorners(boolean visFull) { DegreePosition[] result = new DegreePosition[2]; result[0] = new DegreePosition(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); result[1] = new DegreePosition(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); return result; } - @Override - public void setVisNE(Position visNE) { - this.visNE = visNE; - } - - @Override - public void setVisSW(Position visSW) { - this.visSW = visSW; - } - - @Override - public void setVisFullCanvas(boolean full) { - } - @Override public double getParticleFactor() { return this.particleFactor; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index a2dcff6ed23..f6ab2ba3881 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -3,8 +3,6 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import java.util.List; import com.google.gwt.core.client.GWT; -import com.google.gwt.json.client.JSONObject; -import com.google.gwt.json.client.JSONParser; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; @@ -31,11 +29,6 @@ public class SimulatorField implements VectorField { private Position bdC; private double xScale; - - private Position visSW; - private Position visNE; - private boolean visFull = false; - private double maxLength; private double particleFactor; @@ -52,25 +45,19 @@ public class SimulatorField implements VectorField { public SimulatorField(WindFieldDTO windData, WindFieldGenParamsDTO windParams) { this.colorsForSpeeds = createColorsForSpeeds(); this.step = 0; - String parseString = windData.windDataJSON.substring(18, windData.windDataJSON.length() - 1) + "}"; - JSONObject baseData = JSONParser.parseLenient(parseString).isObject(); - this.rcStart = new DegreePosition(baseData.get("rcStart").isObject().get("lat").isNumber().doubleValue(), - baseData.get("rcStart").isObject().get("lng").isNumber().doubleValue()); - this.rcEnd = new DegreePosition(baseData.get("rcEnd").isObject().get("lat").isNumber().doubleValue(), baseData - .get("rcEnd").isObject().get("lng").isNumber().doubleValue()); - this.resY = (int) baseData.get("resY").isNumber().doubleValue(); - this.resX = (int) baseData.get("resX").isNumber().doubleValue(); - this.borderY = (int) baseData.get("borderY").isNumber().doubleValue(); - this.borderX = (int) baseData.get("borderX").isNumber().doubleValue(); + this.rcStart = new DegreePosition(windData.windData.rcStart.latDeg, windData.windData.rcStart.lngDeg); + this.rcEnd = new DegreePosition(windData.windData.rcEnd.latDeg, windData.windData.rcEnd.lngDeg); + this.resX = windData.windData.resX; + this.resY = windData.windData.resY; + this.borderX = windData.windData.borderX; + this.borderY = windData.windData.borderY; this.bdXi = (this.borderY + 0.5) / (this.resY - 1); this.bdPhi = 1.0 + 2 * this.bdXi; this.bdA = new DegreePosition(this.rcEnd.getLatDeg() + (this.rcEnd.getLatDeg() - this.rcStart.getLatDeg()) * this.bdXi, this.rcEnd.getLngDeg() + (this.rcEnd.getLngDeg() - this.rcStart.getLngDeg()) * this.bdXi); this.bdB = new DegreePosition((this.rcStart.getLatDeg() - this.rcEnd.getLatDeg()) * this.bdPhi, (this.rcStart.getLngDeg() - this.rcEnd.getLngDeg()) * this.bdPhi); - this.xScale = baseData.get("xScale").isNumber().doubleValue(); - this.visSW = new DegreePosition(0.0, 0.0); - this.visNE = new DegreePosition(0.0, 0.0); + this.xScale = windData.windData.xScale; List gridData = windData.getMatrix(); int p = 0; int imax = windParams.getyRes() + 2 * windParams.getBorderY(); @@ -115,36 +102,19 @@ public class SimulatorField implements VectorField { this.gvX.getLngDeg() * (this.resX + 2 * this.borderX - 1) / (this.resX - 1)); } - @Override - public Position getRandomPosition() { - final Position result; - if (this.visFull) { - double rndY = Math.random(); - double rndX = Math.random(); - double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); - double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); - result = new DegreePosition(latDeg, lngDeg); - } else { - double rndY = Math.random(); - double rndX = Math.random() - 0.5; - result = this.getInnerPosition(rndX, rndY); - } - return result; - } - - private Position getInnerPosition(double factX, double factY) { + private Position getInnerPosition(double factX, double factY, boolean visFull) { double latDeg = this.bdA.getLatDeg() + factY * this.bdB.getLatDeg() + factX * this.bdC.getLatDeg(); double lngDeg = this.bdA.getLngDeg() + factY * this.bdB.getLngDeg() + factX * this.bdC.getLngDeg(); Position result = new DegreePosition(latDeg, lngDeg); - if (swarmDebug && (!this.inBounds(result))) { + if (swarmDebug && (!this.inBounds(result, visFull))) { GWT.log("random-position: out of bounds"); } return result; } @Override - public boolean inBounds(Position p) { - Index idx = this.getIndex(p); + public boolean inBounds(Position p, boolean visFull) { + Index idx = this.getIndex(p, visFull); boolean inBool = (idx.x >= 0) && (idx.x < (this.resX + 2 * this.borderX)) && (idx.y >= 0) && (idx.y < (this.resY + 2 * this.borderY)); return inBool; @@ -197,7 +167,7 @@ public class SimulatorField implements VectorField { return this.interpolate(p); } - private Index getIndex(Position p) { + private Index getIndex(Position p, boolean visFull) { // calculate grid indexes Position posR = new DegreePosition(p.getLatDeg() - this.rcStart.getLatDeg(), (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale); @@ -209,7 +179,7 @@ public class SimulatorField implements VectorField { + (this.resX - 1) / 2.) + this.borderX; - if (this.visFull) { + if (visFull) { if (yIdx >= (this.resY + 2 * this.borderY)) { yIdx = this.resY + 2 * this.borderY - 1; } @@ -321,11 +291,11 @@ public class SimulatorField implements VectorField { } @Override - public Position[] getFieldCorners() { - Position fieldNE = this.getInnerPosition(+0.5, 1.0); - Position fieldSW = this.getInnerPosition(-0.5, 0.0); - Position fieldSE = this.getInnerPosition(+0.5, 0.0); - Position fieldNW = this.getInnerPosition(-0.5, 1.0); + public Position[] getFieldCorners(boolean visFull) { + Position fieldNE = this.getInnerPosition(+0.5, 1.0, visFull); + Position fieldSW = this.getInnerPosition(-0.5, 0.0, visFull); + Position fieldSE = this.getInnerPosition(+0.5, 0.0, visFull); + Position fieldNW = this.getInnerPosition(-0.5, 1.0, visFull); DegreePosition[] result = new DegreePosition[2]; @@ -344,24 +314,6 @@ public class SimulatorField implements VectorField { return result; } - @Override - public void setVisNE(Position visNE) { - this.visNE = visNE; - } - - @Override - public void setVisSW(Position visSW) { - this.visSW = visSW; - } - - @Override - public void setVisFullCanvas(boolean full) { - if (this.visFull != full) { - System.out.println("visFull: " + full); - this.visFull = full; - } - } - @Override public double getParticleFactor() { return this.particleFactor; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index af3cc7e388d..071ac155584 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -28,6 +28,9 @@ public class Swarm { private boolean swarmContinue = true; private Position boundsNE; private Position boundsSW; + private Position visNE; + private boolean visFull; + private Position visSW; public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { this.fullcanvas = fullcanvas; @@ -67,7 +70,7 @@ public class Swarm { Particle particle = new Particle(); boolean done = false; while (!done) { - particle.pos = field.getRandomPosition(); + particle.pos = getRandomPosition(); Vector v = field.getVector(particle.pos); double weight = field.particleWeight(particle.pos, v); if (weight >= Math.random()) { @@ -84,6 +87,27 @@ public class Swarm { } return particle; } + + private Position getRandomPosition() { + final Position result; + if (isVisFull()) { + double rndY = Math.random(); + double rndX = Math.random(); + double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); + double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); + result = new DegreePosition(latDeg, lngDeg); + } else { + double rndY = Math.random(); + double rndX = Math.random() - 0.5; + result = field.getInnerPosition(rndX, rndY, isVisFull()); + } + return result; + } + + + private boolean isVisFull() { + return visFull; + } public Particle[] createParticles() { Particle[] newParticles = new Particle[nParticles]; @@ -103,7 +127,7 @@ public class Swarm { .getNorthEast().getLongitude()); Position mapSW = new DegreePosition(map.getBounds().getSouthWest().getLatitude(), map.getBounds() .getSouthWest().getLongitude()); - Position[] fieldCorners = this.field.getFieldCorners(); + Position[] fieldCorners = this.field.getFieldCorners(visFull); Position fieldNE = fieldCorners[1]; Position fieldSW = fieldCorners[0]; Vector visibleNE = this.isVisible(fieldNE); @@ -136,9 +160,9 @@ public class Swarm { this.boundsSW = fieldSW; } } - this.field.setVisSW(this.boundsSW); - this.field.setVisNE(this.boundsNE); - this.field.setVisFullCanvas((!useBoundsNorth) && (!useBoundsEast) && (!useBoundsSouth) && (!useBoundsWest)); + this.setVisSW(this.boundsSW); + this.setVisNE(this.boundsNE); + this.setVisFullCanvas((!useBoundsNorth) && (!useBoundsEast) && (!useBoundsSouth) && (!useBoundsWest)); Vector boundsSWpx = this.projection.latlng2pixel(this.boundsSW); Vector boundsNEpx = this.projection.latlng2pixel(this.boundsNE); double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); @@ -146,6 +170,18 @@ public class Swarm { this.nParticles = (int) Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); }; + private void setVisFullCanvas(boolean visFull) { + this.visFull = visFull; + } + + private void setVisNE(Position visNE) { + this.visNE = visNE; + } + + private void setVisSW(Position visSW) { + this.visSW = visSW; + } + public Vector isVisible(Position pos) { // test for visibility of swarm Vector proj = this.projection.latlng2pixel(pos); @@ -219,7 +255,7 @@ public class Swarm { particle.pos = new DegreePosition(latDeg, lngDeg); particle.speed = particle.v.length(); particle.age--; - if ((particle.age > 0) && (this.field.inBounds(particle.pos))) { + if ((particle.age > 0) && (this.field.inBounds(particle.pos, isVisFull()))) { particle.v = field.getVector(particle.pos); } else { particle.v = null; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index a3ad3708472..34b98359620 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -3,9 +3,7 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import com.sap.sailing.domain.common.Position; public interface VectorField { - Position getRandomPosition(); - - boolean inBounds(Position p); + boolean inBounds(Position p, boolean visFull); Vector getVector(Position p); @@ -17,13 +15,7 @@ public interface VectorField { double lineWidth(double speed); - Position[] getFieldCorners(); - - void setVisNE(Position visNE); - - void setVisSW(Position visSW); - - void setVisFullCanvas(boolean full); + Position[] getFieldCorners(boolean visFull); double getParticleFactor(); From 51d801800e5ac3c7849f1248a19b6ff5d4d1002e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 23:14:22 +0200 Subject: [PATCH 007/110] don't use Date as as duration; use Duration instead --- .../gwt/ui/server/SimulatorServiceImpl.java | 71 +++++++----------- .../gwt/ui/shared/WindFieldGenParamsDTO.java | 9 +-- .../gwt/ui/simulator/PathCanvasOverlay.java | 2 +- .../gwt/ui/simulator/SimulatorMainPanel.java | 6 +- .../WindStreamletsCanvasOverlay.java | 3 +- .../simulator/streamlets/SimulatorField.java | 72 ++++++++++--------- .../windfield/WindFieldGenerator.java | 3 +- 7 files changed, 77 insertions(+), 89 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java index 08536b403b9..0620b7f5082 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java @@ -14,6 +14,7 @@ import java.util.logging.Logger; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; @@ -203,13 +204,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula Position[][] grid = bd.extractGrid(params.getxRes(), params.getyRes(), params.getBorderY(), params.getBorderX()); wf.setPositionGrid(grid); - TimePoint startTime = new MillisecondsTimePoint(params.getStartTime().getTime());// new - // MillisecondsTimePoint(0); - TimePoint timeStep = new MillisecondsTimePoint(params.getTimeStep().getTime());// new - // MillisecondsTimePoint(30*1000); - TimePoint endTime = new MillisecondsTimePoint(params.getEndTime().getTime());// new MillisecondsTimePoint(10 * - // 60 * 1000); - + TimePoint startTime = new MillisecondsTimePoint(params.getStartTime().getTime()); + Duration timeStep = params.getTimeStep(); + TimePoint endTime = new MillisecondsTimePoint(params.getEndTime().getTime()); wf.generate(startTime, null, timeStep); if (params.getMode() != SailingSimulatorConstants.ModeMeasured) { @@ -218,8 +215,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula wf.setGridAreaGps(gridAreaGps); } - WindFieldDTO wfDTO = createWindFieldDTO(wf, startTime, endTime, timeStep, params); //params.isShowStreamlets2(), params.isShowLines(), params.getSeedLines()); - + WindFieldDTO wfDTO = createWindFieldDTO(wf, startTime, endTime, timeStep, params); LOGGER.info("Exiting getWindField"); return wfDTO; @@ -250,7 +246,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula WindFieldGenerator wf = null; List course = null; TimePoint startTime = new MillisecondsTimePoint(params.getStartTime().getTime()); - TimePoint timeStep = new MillisecondsTimePoint(params.getTimeStep().getTime()); + Duration timeStep = params.getTimeStep(); this.controlParameters.resetBlastRandomStream = params.isKeepState(); this.retreiveWindControlParameters(pattern); @@ -725,53 +721,37 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula return result; } - private WindFieldDTO createWindFieldDTO(WindFieldGenerator wf, TimePoint startTime, TimePoint endTime, TimePoint timeStep, WindFieldGenParamsDTO params) { - //boolean isStreamlets, boolean isShowLines, char seedLines) { - + private WindFieldDTO createWindFieldDTO(WindFieldGenerator wf, TimePoint startTime, TimePoint endTime, + Duration timeStep, WindFieldGenParamsDTO params) { WindFieldDTO windFieldDTO = new WindFieldDTO(); Position[][] positionGrid = wf.getPositionGrid(); - - //if (!params.isShowStreamlets2()) { - List wList = new ArrayList(); - if (positionGrid != null && positionGrid.length > 0) { - TimePoint t = startTime; - while (t.compareTo(endTime) <= 0) { - for (int i = 0; i < positionGrid.length; ++i) { - for (int j = 0; j < positionGrid[i].length; ++j) { - Wind localWind = wf.getWind(new TimedPositionWithSpeedImpl(t, positionGrid[i][j], null)); - LOGGER.finer(localWind.toString()); - wList.add(createSimulatorWindDTO(localWind)); - } - } - t = new MillisecondsTimePoint(t.asMillis() + timeStep.asMillis()); - } + TimePoint t = startTime; + while (t.compareTo(endTime) <= 0) { + for (int i = 0; i < positionGrid.length; ++i) { + for (int j = 0; j < positionGrid[i].length; ++j) { + Wind localWind = wf.getWind(new TimedPositionWithSpeedImpl(t, positionGrid[i][j], null)); + LOGGER.finer(localWind.toString()); + wList.add(createSimulatorWindDTO(localWind)); + } + } + t = new MillisecondsTimePoint(t.asMillis() + timeStep.asMillis()); + } } - windFieldDTO.setMatrix(wList); - if (params.isShowLines() && params.getSeedLines() == 'f') { - this.getWindLinesFromStartLine(wf, windFieldDTO, startTime, endTime, timeStep); + this.getWindLinesFromStartLine(wf, windFieldDTO, startTime, endTime, timeStep); } - if (params.isShowLines() && params.getSeedLines() == 'b') { - this.getWindLinesFromEndLine(wf, windFieldDTO, startTime, endTime, timeStep); + this.getWindLinesFromEndLine(wf, windFieldDTO, startTime, endTime, timeStep); } - windFieldDTO.curBearing = wf.getWindParameters().curBearing; windFieldDTO.curSpeed = wf.getWindParameters().curSpeed; - - //} else { if (params.isShowStreamlets2()) { WindData windData = new WindData(); windData.rcStart = params.getNorthWest(); windData.rcEnd = params.getSouthEast(); - // SouthWest - jsonField += "boundsSW:{lat:" + positionGrid[0][0].getLatDeg() + ",lng:" + positionGrid[0][0].getLngDeg() + "},"; - // NorthEast - jsonField += "boundsNE:{lat:" + positionGrid[params.getyRes()+2*params.getBorderY()-1][params.getxRes()+2*params.getBorderX()-1].getLatDeg() + ",lng:" + positionGrid[params.getyRes()+2*params.getBorderY()-1][params.getxRes()+2*params.getBorderX()-1].getLngDeg() + "},"; - //this.resY = field.resY; windData.resX = params.getxRes(); windData.resY = params.getyRes(); windData.borderX = params.getBorderX(); @@ -779,12 +759,11 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula windData.xScale = 1.5; windFieldDTO.windData = windData; } - return windFieldDTO; } - private void getWindLinesFromStartLine(WindFieldGenerator wf, WindFieldDTO windFieldDTO, TimePoint startTime, TimePoint endTime, TimePoint timeStep) { - + private void getWindLinesFromStartLine(WindFieldGenerator wf, WindFieldDTO windFieldDTO, TimePoint startTime, + TimePoint endTime, Duration timeStep) { Position[][] positionGrid = wf.getPositionGrid(); WindLinesDTO windLinesDTO = windFieldDTO.getWindLinesDTO(); if (windLinesDTO == null) { @@ -822,8 +801,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula //logger.info("Added : " + windFieldDTO.getWindLinesDTO().getWindLinesMap().size() + " wind lines"); } - private void getWindLinesFromEndLine(WindFieldGenerator wf, WindFieldDTO windFieldDTO, TimePoint startTime, TimePoint endTime, TimePoint timeStep) { - + private void getWindLinesFromEndLine(WindFieldGenerator wf, WindFieldDTO windFieldDTO, TimePoint startTime, + TimePoint endTime, Duration timeStep) { Position[][] positionGrid = wf.getPositionGrid(); WindLinesDTO windLinesDTO = windFieldDTO.getWindLinesDTO(); if (windLinesDTO == null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldGenParamsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldGenParamsDTO.java index 736d87e169a..3e0d7935891 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldGenParamsDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldGenParamsDTO.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.shared; import java.util.Date; import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.dto.PositionDTO; public class WindFieldGenParamsDTO implements IsSerializable { @@ -17,7 +18,7 @@ public class WindFieldGenParamsDTO implements IsSerializable { private Date startTime; private Date endTime; - private Date timeStep; + private Duration timeStep; private boolean keepState; @@ -111,15 +112,15 @@ public class WindFieldGenParamsDTO implements IsSerializable { this.endTime = endTime; } - public Date getTimeStep() { + public Duration getTimeStep() { return timeStep; } - public void setTimeStep(Date timeStep) { + public void setTimeStep(Duration timeStep) { this.timeStep = timeStep; } - public void setDefaultTimeSettings(Date startTime, Date timeStep, Date endTime) { + public void setDefaultTimeSettings(Date startTime, Duration timeStep, Date endTime) { this.startTime = startTime; this.timeStep = timeStep; this.endTime = endTime; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java index 0ec3dfb28a2..ad974392326 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java @@ -173,7 +173,7 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { } index++; - long timeStep = windParams.getTimeStep().getTime(); + long timeStep = windParams.getTimeStep().asMillis(); if ((windDTO.timepoint - startTime) % (timeStep) == 0) { drawPoint(windDTO); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java index 5fe66c5656f..6b69aa3a720 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java @@ -42,7 +42,9 @@ import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.dto.BoatClassDTO; +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SimulatorServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; @@ -306,7 +308,7 @@ public class SimulatorMainPanel extends SimplePanel { TimeRangeWithZoomProvider timeRangeProvider = new TimeRangeWithZoomModel(); initTimer(); timer.setTime(windParams.getStartTime().getTime()); - int secondsTimeStep = (int) windParams.getTimeStep().getTime() / 1000; + int secondsTimeStep = (int) windParams.getTimeStep().asSeconds(); timer.setPlaySpeedFactor(secondsTimeStep); timePanel = new TimePanel(timer, timeRangeProvider, stringMessages, false); @@ -363,7 +365,7 @@ public class SimulatorMainPanel extends SimplePanel { long epochTime = (defaultNow.getTime()/86400000)*86400000 - utcOffSet*3600000; // today, midnight in default time zone Date startTime = new Date(epochTime); - Date timeStep = new Date(15 * 1000); + Duration timeStep = new MillisecondsDurationImpl(15 * 1000); Date endTime = new Date(startTime.getTime() + 10 * 60 * 1000); windParams.setDefaultTimeSettings(startTime, timeStep, endTime); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 1b4c600ddfd..5db5e5f09a5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -136,8 +136,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti @Override public void timeChanged(final Date newDate, Date oldDate) { - int step = (int) ((newDate.getTime() - this.windParams.getStartTime().getTime()) / this.windParams - .getTimeStep().getTime()); + int step = (int) ((newDate.getTime() - this.windParams.getStartTime().getTime()) / this.windParams.getTimeStep().asMillis()); this.setStreamletsStep(step); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index f6ab2ba3881..23f6608b0e8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -15,30 +15,34 @@ public class SimulatorField implements VectorField { private Position rcStart; private Position rcEnd; - private int resY; - private int resX; + private final int resY; + private final int resX; - private int borderY; - private int borderX; + private final int borderY; + private final int borderX; - private double bdXi; - private double bdPhi; + private final double bdXi; + private final double bdPhi; - private Position bdA; - private Position bdB; - private Position bdC; + private final Position bdA; + private final Position bdB; + private final Position bdC; - private double xScale; - private double maxLength; - private double particleFactor; + private final double xScale; + private final double maxLength; + private final double particleFactor; - private double lngScale; + /** + * The cosine of the field's average latitude (arithmetic average of NW's latitude and SE's latitude) which represents + * the average ratio of pixels used per longitude and latitude angle in the Mercator projection. At the equator, this + * ration is 1; towards the poles it gets less. + */ + private final double lngScale; - private Position nvY; - private Position nvX; - private Position gvX; + private final Position nvY; + private final Position nvX; - private double[][][] data; + private final double[][][] data; private final String[] colorsForSpeeds; private int step; @@ -65,7 +69,8 @@ public class SimulatorField implements VectorField { int steps = gridData.size() / (imax * jmax); this.data = new double[steps][imax][2 * jmax]; double maxWindSpeed = 0; - double minWindSpeed = 100; + double minWindSpeed = Double.MAX_VALUE; + // copy the wind data from WindFieldDTO.getMatrix() to the data array, determining min/max wind speed for (int s = 0; s < steps; s++) { for (int i = 0; i < imax; i++) { for (int j = 0; j < jmax; j++) { @@ -86,20 +91,20 @@ public class SimulatorField implements VectorField { } this.maxLength = maxWindSpeed; this.particleFactor = 2.0; - double latAvg = (this.rcEnd.getLatDeg() + this.rcStart.getLatDeg()) / 2.; + final double latAvg = (this.rcEnd.getLatDeg() + this.rcStart.getLatDeg()) / 2.; this.lngScale = Math.cos(latAvg * Math.PI / 180.0); - double difLat = this.rcEnd.getLatDeg() - this.rcStart.getLatDeg(); - double difLng = (this.rcEnd.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale; - double difLen = Math.sqrt(difLat * difLat + difLng * difLng); + final double difLat = this.rcEnd.getLatDeg() - this.rcStart.getLatDeg(); + final double difLng = (this.rcEnd.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale; + final double difLen = Math.sqrt(difLat * difLat + difLng * difLng); this.nvY = new DegreePosition(difLat / difLen / difLen * (this.resY - 1), difLng / difLen / difLen * (this.resY - 1)); double nrmLat = -difLng / difLen; double nrmLng = difLat / difLen; this.nvX = new DegreePosition(nrmLat / this.xScale / difLen * (this.resX - 1), nrmLng / this.xScale / difLen * (this.resX - 1)); - this.gvX = new DegreePosition(nrmLat * this.xScale * difLen, nrmLng / this.lngScale * this.xScale * difLen); - this.bdC = new DegreePosition(this.gvX.getLatDeg() * (this.resX + 2 * this.borderX - 1) / (this.resX - 1), - this.gvX.getLngDeg() * (this.resX + 2 * this.borderX - 1) / (this.resX - 1)); + final Position gvX = new DegreePosition(nrmLat * this.xScale * difLen, nrmLng / this.lngScale * this.xScale * difLen); + this.bdC = new DegreePosition(gvX.getLatDeg() * (this.resX + 2 * this.borderX - 1) / (this.resX - 1), + gvX.getLngDeg() * (this.resX + 2 * this.borderX - 1) / (this.resX - 1)); } private Position getInnerPosition(double factX, double factY, boolean visFull) { @@ -126,14 +131,15 @@ public class SimulatorField implements VectorField { && ((idx.xTop >= (this.resX + 2 * this.borderX)) || (idx.yTop >= (this.resY + 2 * this.borderY)))) { GWT.log("interpolate: out of range: " + idx.xTop + " " + idx.yTop); } - double avgX = this.data[this.step][idx.yBot][2 * idx.xBot] * (1 - idx.yMod) * (1 - idx.xMod) - + this.data[this.step][idx.yTop][2 * idx.xBot] * idx.yMod * (1 - idx.xMod) - + this.data[this.step][idx.yBot][2 * idx.xTop] * (1 - idx.yMod) * idx.xMod - + this.data[this.step][idx.yTop][2 * idx.xTop] * idx.yMod * idx.xMod; - double avgY = this.data[this.step][idx.yBot][2 * idx.xBot + 1] * (1 - idx.yMod) * (1 - idx.xMod) - + this.data[this.step][idx.yTop][2 * idx.xBot + 1] * idx.yMod * (1 - idx.xMod) - + this.data[this.step][idx.yBot][2 * idx.xTop + 1] * (1 - idx.yMod) * idx.xMod - + this.data[this.step][idx.yTop][2 * idx.xTop + 1] * idx.yMod * idx.xMod; + final double[][] dataAtStep = this.data[this.step]; + double avgX = dataAtStep[idx.yBot][2 * idx.xBot] * (1 - idx.yMod) * (1 - idx.xMod) + + dataAtStep[idx.yTop][2 * idx.xBot] * idx.yMod * (1 - idx.xMod) + + dataAtStep[idx.yBot][2 * idx.xTop] * (1 - idx.yMod) * idx.xMod + + dataAtStep[idx.yTop][2 * idx.xTop] * idx.yMod * idx.xMod; + double avgY = dataAtStep[idx.yBot][2 * idx.xBot + 1] * (1 - idx.yMod) * (1 - idx.xMod) + + dataAtStep[idx.yTop][2 * idx.xBot + 1] * idx.yMod * (1 - idx.xMod) + + dataAtStep[idx.yBot][2 * idx.xTop + 1] * (1 - idx.yMod) * idx.xMod + + dataAtStep[idx.yTop][2 * idx.xTop + 1] * idx.yMod * idx.xMod; return new Vector(avgX / this.lngScale, avgY); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java index acac4f5edb6..7aa17261023 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java @@ -4,6 +4,7 @@ package com.sap.sailing.simulator.windfield; import java.io.Serializable; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.simulator.Boundary; @@ -16,7 +17,7 @@ public interface WindFieldGenerator extends WindField, Serializable { public void setPositionGrid(Position[][] positions); - public void generate(TimePoint start, TimePoint end, TimePoint step); + public void generate(TimePoint start, TimePoint end, Duration step); public Position[][] getPositionGrid(); From 0e338cca06249ecc4ae8974e36e14b6b9da5831d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 23:18:09 +0200 Subject: [PATCH 008/110] turn bdXi and bdPhi to local variables instead of fields; they weren't accessed outside the constructor --- .../gwt/ui/simulator/streamlets/SimulatorField.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 23f6608b0e8..b8b182fdd3e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -21,9 +21,6 @@ public class SimulatorField implements VectorField { private final int borderY; private final int borderX; - private final double bdXi; - private final double bdPhi; - private final Position bdA; private final Position bdB; private final Position bdC; @@ -55,12 +52,12 @@ public class SimulatorField implements VectorField { this.resY = windData.windData.resY; this.borderX = windData.windData.borderX; this.borderY = windData.windData.borderY; - this.bdXi = (this.borderY + 0.5) / (this.resY - 1); - this.bdPhi = 1.0 + 2 * this.bdXi; + final double bdXi = (this.borderY + 0.5) / (this.resY - 1); + final double bdPhi = 1.0 + 2 * bdXi; this.bdA = new DegreePosition(this.rcEnd.getLatDeg() + (this.rcEnd.getLatDeg() - this.rcStart.getLatDeg()) - * this.bdXi, this.rcEnd.getLngDeg() + (this.rcEnd.getLngDeg() - this.rcStart.getLngDeg()) * this.bdXi); - this.bdB = new DegreePosition((this.rcStart.getLatDeg() - this.rcEnd.getLatDeg()) * this.bdPhi, - (this.rcStart.getLngDeg() - this.rcEnd.getLngDeg()) * this.bdPhi); + * bdXi, this.rcEnd.getLngDeg() + (this.rcEnd.getLngDeg() - this.rcStart.getLngDeg()) * bdXi); + this.bdB = new DegreePosition((this.rcStart.getLatDeg() - this.rcEnd.getLatDeg()) * bdPhi, + (this.rcStart.getLngDeg() - this.rcEnd.getLngDeg()) * bdPhi); this.xScale = windData.windData.xScale; List gridData = windData.getMatrix(); int p = 0; From 6e7b9ccc78f26306d5a2b361881e916d32739c9c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 23:42:28 +0200 Subject: [PATCH 009/110] don't use Position for an offset --- .../gwt/ui/simulator/streamlets/SimulatorField.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index b8b182fdd3e..1ac8ddd49b0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -123,7 +123,7 @@ public class SimulatorField implements VectorField { } private Vector interpolate(Position p) { - Neighbors idx = this.getNeighbors(p); + Neighbors idx = getNeighbors(p); if (swarmDebug && ((idx.xTop >= (this.resX + 2 * this.borderX)) || (idx.yTop >= (this.resY + 2 * this.borderY)))) { GWT.log("interpolate: out of range: " + idx.xTop + " " + idx.yTop); @@ -172,13 +172,12 @@ public class SimulatorField implements VectorField { private Index getIndex(Position p, boolean visFull) { // calculate grid indexes - Position posR = new DegreePosition(p.getLatDeg() - this.rcStart.getLatDeg(), - (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale); - + final double latOffset = p.getLatDeg() - this.rcStart.getLatDeg(); + final double lngOffset = (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale; // closest grid point - long yIdx = Math.round(posR.getLatDeg() * this.nvY.getLatDeg() + posR.getLngDeg() * this.nvY.getLngDeg()) + long yIdx = Math.round(latOffset * this.nvY.getLatDeg() + lngOffset * this.nvY.getLngDeg()) + this.borderY; - long xIdx = Math.round(posR.getLatDeg() * this.nvX.getLatDeg() + posR.getLngDeg() * this.nvX.getLngDeg() + long xIdx = Math.round(latOffset * this.nvX.getLatDeg() + lngOffset * this.nvX.getLngDeg() + (this.resX - 1) / 2.) + this.borderX; From 446eeade1886b5839e37b18a37d20a83740292fb Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jun 2014 23:52:31 +0200 Subject: [PATCH 010/110] made Swarm methods private in case they were not used outside of class --- .../ui/simulator/streamlets/SimulatorField.java | 8 ++------ .../sailing/gwt/ui/simulator/streamlets/Swarm.java | 14 +++++++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 1ac8ddd49b0..07e9504c962 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -175,12 +175,8 @@ public class SimulatorField implements VectorField { final double latOffset = p.getLatDeg() - this.rcStart.getLatDeg(); final double lngOffset = (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale; // closest grid point - long yIdx = Math.round(latOffset * this.nvY.getLatDeg() + lngOffset * this.nvY.getLngDeg()) - + this.borderY; - long xIdx = Math.round(latOffset * this.nvX.getLatDeg() + lngOffset * this.nvX.getLngDeg() - + (this.resX - 1) / 2.) - + this.borderX; - + long yIdx = Math.round(latOffset * this.nvY.getLatDeg() + lngOffset * this.nvY.getLngDeg()) + this.borderY; + long xIdx = Math.round(latOffset * this.nvX.getLatDeg() + lngOffset * this.nvX.getLngDeg() + (this.resX - 1) / 2.) + this.borderX; if (visFull) { if (yIdx >= (this.resY + 2 * this.borderY)) { yIdx = this.resY + 2 * this.borderY - 1; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 071ac155584..9afee28f67c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -66,7 +66,7 @@ public class Swarm { this.swarmContinue = false; } - public Particle createParticle() { + private Particle createParticle() { Particle particle = new Particle(); boolean done = false; while (!done) { @@ -109,7 +109,7 @@ public class Swarm { return visFull; } - public Particle[] createParticles() { + private Particle[] createParticles() { Particle[] newParticles = new Particle[nParticles]; for (int idx = 0; idx < newParticles.length; idx++) { newParticles[idx] = this.createParticle(); @@ -122,7 +122,7 @@ public class Swarm { swarmPause = 5; } - public void updateBounds() { + private void updateBounds() { Position mapNE = new DegreePosition(map.getBounds().getNorthEast().getLatitude(), map.getBounds() .getNorthEast().getLongitude()); Position mapSW = new DegreePosition(map.getBounds().getSouthWest().getLatitude(), map.getBounds() @@ -182,7 +182,7 @@ public class Swarm { this.visSW = visSW; } - public Vector isVisible(Position pos) { + private Vector isVisible(Position pos) { // test for visibility of swarm Vector proj = this.projection.latlng2pixel(pos); Vector result = new Vector(); @@ -191,7 +191,7 @@ public class Swarm { return result; } - public void startLoop(final int millis) { + private void startLoop(final int millis) { // Create animation-loop based on timer timeout loopTimer = new com.google.gwt.user.client.Timer() { public void run() { @@ -221,7 +221,7 @@ public class Swarm { loopTimer.schedule(millis); } - public void drawSwarm() { + private void drawSwarm() { Context2d ctxt = canvas.getContext2d(); ctxt.setGlobalAlpha(0.08); ctxt.setGlobalCompositeOperation("destination-out"); @@ -245,7 +245,7 @@ public class Swarm { } } - public boolean execute() { + private boolean execute() { double speed = 0.01 * field.motionScale(map.getZoom()); for (int idx = 0; idx < particles.length; idx++) { Particle particle = particles[idx]; From 1a7a2c3ad8a54ca42ba974ad1db21d51278eea61 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 00:29:30 +0200 Subject: [PATCH 011/110] added comments --- .../gwt/ui/simulator/SimulatorMap.java | 2 +- .../WindStreamletsCanvasOverlay.java | 3 +- .../gwt/ui/simulator/streamlets/Swarm.java | 28 +++++++++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index ded9d16b481..9f6462fd923 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -549,7 +549,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ windLineGuidesCanvasOverlay = new WindLineGuidesCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDSTREAMLETS_ZINDEX, timer, xRes); } if (windParams.isShowStreamlets2()) { - windStreamletsCanvasOverlay = new WindStreamletsCanvasOverlay(this, SimulatorMapOverlaysZIndexes.WINDFIELD_ZINDEX, timer, windParams); + windStreamletsCanvasOverlay = new WindStreamletsCanvasOverlay(this, SimulatorMapOverlaysZIndexes.WINDFIELD_ZINDEX, timer, windParams); } if (windParams.isShowGrid()) { windGridCanvasOverlay = new WindGridCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDGRID_ZINDEX, timer, xRes, yRes); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 5db5e5f09a5..1dffa560323 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -28,8 +28,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private int nParticles; private Swarm swarm; - public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, - final WindFieldGenParamsDTO windParams) { + public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) { super(simulatorMap.getMap(), zIndex); this.simulatorMap = simulatorMap; this.timer = timer; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 9afee28f67c..ea65a9606a4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -12,6 +12,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; +import com.sap.sse.common.Util.Pair; public class Swarm { private final FullCanvasOverlay fullcanvas; @@ -130,13 +131,13 @@ public class Swarm { Position[] fieldCorners = this.field.getFieldCorners(visFull); Position fieldNE = fieldCorners[1]; Position fieldSW = fieldCorners[0]; - Vector visibleNE = this.isVisible(fieldNE); - Vector visibleSW = this.isVisible(fieldSW); - boolean useBoundsNorth = (visibleNE.y == 0); - boolean useBoundsEast = (visibleNE.x == 0); - boolean useBoundsSouth = (visibleSW.y == 0); - boolean useBoundsWest = (visibleSW.x == 0); - swarmOffScreen = (visibleNE.y > 0) || (visibleSW.y < 0) || (visibleNE.x < 0) || (visibleSW.x > 0); + Pair visibleNE = this.isVisible(fieldNE); + Pair visibleSW = this.isVisible(fieldSW); + boolean useBoundsNorth = visibleNE.getB(); + boolean useBoundsEast = visibleNE.getA(); + boolean useBoundsSouth = visibleSW.getB(); + boolean useBoundsWest = visibleSW.getA(); + swarmOffScreen = !visibleNE.getB() || !visibleNE.getA(); if (swarmOffScreen) { this.boundsNE = fieldNE; this.boundsSW = fieldSW; @@ -182,13 +183,15 @@ public class Swarm { this.visSW = visSW; } - private Vector isVisible(Position pos) { + /** + * Tells if pos is within the bounds of {@link #canvas}, for X and Y coordinate. + */ + private Pair isVisible(Position pos) { // test for visibility of swarm Vector proj = this.projection.latlng2pixel(pos); - Vector result = new Vector(); - result.x = (proj.x < 0 ? -1 : 0) + (proj.x > canvas.getOffsetWidth() ? 1 : 0); - result.y = (proj.y < 0 ? -1 : 0) + (proj.y > canvas.getOffsetHeight() ? 1 : 0); - return result; + final boolean xVisible = proj.x >= 0 && proj.x <= canvas.getOffsetWidth(); + final boolean yVisible = proj.y >= 0 && proj.y <= canvas.getOffsetHeight(); + return new Pair(xVisible, yVisible); } private void startLoop(final int millis) { @@ -261,6 +264,7 @@ public class Swarm { particle.v = null; } } else { + // particle timed out (age became 0); create a new one particles[idx] = this.createParticle(); } } From 438f47633e82b642a1256b8f6c6cb99163979c3b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 01:12:55 +0200 Subject: [PATCH 012/110] removed Particle.speed as it is redundant with Particle.v; added comments --- .../WindStreamletsCanvasOverlay.java | 2 +- .../gwt/ui/simulator/streamlets/Particle.java | 7 +- .../gwt/ui/simulator/streamlets/Swarm.java | 90 ++++++++++--------- .../ui/simulator/streamlets/VectorField.java | 40 +++++++++ 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 1dffa560323..95c9c16717c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -121,7 +121,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti if (mapProjection != null) { if ((nParticles > 0) && (swarm == null)) { this.swarm = new Swarm(this, map); - this.swarm.start(40, null); + this.swarm.start(/* animationIntervalMillis */ 40, /* windField */ null); } if (windFieldDTO != null) { // drawing is done by external JavaScript for Streamlets diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java index cc1a018e9aa..541ec08b9b3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java @@ -3,9 +3,8 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import com.sap.sailing.domain.common.Position; public class Particle { - public Position pos; - public Vector pxOld; - public double speed; - public int age; + public Position currentPosition; + public Vector previousPosition; + public int stepsToLive; public Vector v; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index ea65a9606a4..84c90a425d0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -27,8 +27,6 @@ public class Swarm { private boolean swarmOffScreen = false; private int swarmPause = 0; private boolean swarmContinue = true; - private Position boundsNE; - private Position boundsSW; private Position visNE; private boolean visFull; private Position visSW; @@ -39,7 +37,7 @@ public class Swarm { this.map = map; } - public void start(int animationDuration, WindFieldDTO windField) { + public void start(int animationIntervalMillis, WindFieldDTO windField) { projection = new Mercator(fullcanvas, map); if (windField == null) { SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); @@ -60,7 +58,7 @@ public class Swarm { ctxt.setFillStyle("red"); particles = this.createParticles(); this.swarmContinue = true; - startLoop(animationDuration); + startLoop(animationIntervalMillis); } public void stop() { @@ -70,18 +68,18 @@ public class Swarm { private Particle createParticle() { Particle particle = new Particle(); boolean done = false; + // try to create a particle at a random position until the weight is high enough for it to be displayed while (!done) { - particle.pos = getRandomPosition(); - Vector v = field.getVector(particle.pos); - double weight = field.particleWeight(particle.pos, v); + particle.currentPosition = getRandomPosition(); + Vector v = field.getVector(particle.currentPosition); + double weight = field.particleWeight(particle.currentPosition, v); if (weight >= Math.random()) { if (v.length() == 0) { - particle.age = 0; + particle.stepsToLive = 0; } else { - particle.age = 1 + (int) Math.round(Math.random() * 40); + particle.stepsToLive = 1 + (int) Math.round(Math.random() * 40); } - particle.pxOld = projection.latlng2pixel(particle.pos); - particle.speed = 0; + particle.previousPosition = projection.latlng2pixel(particle.currentPosition); particle.v = v; done = true; } @@ -138,34 +136,36 @@ public class Swarm { boolean useBoundsSouth = visibleSW.getB(); boolean useBoundsWest = visibleSW.getA(); swarmOffScreen = !visibleNE.getB() || !visibleNE.getA(); + final Position boundsNE; + final Position boundsSW; if (swarmOffScreen) { - this.boundsNE = fieldNE; - this.boundsSW = fieldSW; + boundsNE = fieldNE; + boundsSW = fieldSW; } else { if ((!useBoundsNorth) && (!useBoundsEast)) { - this.boundsNE = mapNE; + boundsNE = mapNE; } else if (!useBoundsNorth) { - this.boundsNE = new DegreePosition(mapNE.getLatDeg(), fieldNE.getLngDeg()); + boundsNE = new DegreePosition(mapNE.getLatDeg(), fieldNE.getLngDeg()); } else if (!useBoundsEast) { - this.boundsNE = new DegreePosition(fieldNE.getLatDeg(), mapNE.getLngDeg()); + boundsNE = new DegreePosition(fieldNE.getLatDeg(), mapNE.getLngDeg()); } else { - this.boundsNE = fieldNE; + boundsNE = fieldNE; } if ((!useBoundsSouth) && (!useBoundsWest)) { - this.boundsSW = mapSW; + boundsSW = mapSW; } else if (!useBoundsSouth) { - this.boundsSW = new DegreePosition(mapSW.getLatDeg(), fieldSW.getLngDeg()); + boundsSW = new DegreePosition(mapSW.getLatDeg(), fieldSW.getLngDeg()); } else if (!useBoundsWest) { - this.boundsSW = new DegreePosition(fieldSW.getLatDeg(), mapSW.getLngDeg()); + boundsSW = new DegreePosition(fieldSW.getLatDeg(), mapSW.getLngDeg()); } else { - this.boundsSW = fieldSW; + boundsSW = fieldSW; } } - this.setVisSW(this.boundsSW); - this.setVisNE(this.boundsNE); + this.setVisSW(boundsSW); + this.setVisNE(boundsNE); this.setVisFullCanvas((!useBoundsNorth) && (!useBoundsEast) && (!useBoundsSouth) && (!useBoundsWest)); - Vector boundsSWpx = this.projection.latlng2pixel(this.boundsSW); - Vector boundsNEpx = this.projection.latlng2pixel(this.boundsNE); + Vector boundsSWpx = this.projection.latlng2pixel(boundsSW); + Vector boundsNEpx = this.projection.latlng2pixel(boundsNE); double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); this.nParticles = (int) Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); @@ -194,7 +194,7 @@ public class Swarm { return new Pair(xVisible, yVisible); } - private void startLoop(final int millis) { + private void startLoop(final int animationIntervalMillis) { // Create animation-loop based on timer timeout loopTimer = new com.google.gwt.user.client.Timer() { public void run() { @@ -208,20 +208,18 @@ public class Swarm { particles = createParticles(); swarmPause = 0; } - // console("swarmOffScreen:"+swarmOffScreen); if ((!swarmOffScreen) && (swarmPause == 0)) { execute(); } Date time1 = new Date(); - // console("delta:"+(time1.getTime()-time0.getTime())+"/"+millis); if (swarmContinue) { - loopTimer.schedule((int) Math.max(10, millis - (time1.getTime() - time0.getTime()))); + loopTimer.schedule((int) Math.max(10, animationIntervalMillis - (time1.getTime() - time0.getTime()))); } else { projection.clearCanvas(); } } }; - loopTimer.schedule(millis); + loopTimer.schedule(animationIntervalMillis); } private void drawSwarm() { @@ -235,31 +233,35 @@ public class Swarm { ctxt.setFillStyle("white"); for (int idx = 0; idx < particles.length; idx++) { Particle particle = particles[idx]; - if (particle.age == 0) { + if (particle.stepsToLive == 0) { continue; } - ctxt.setLineWidth(field.lineWidth(particle.speed)); - ctxt.setStrokeStyle(field.getColor(particle.speed)); + double particleSpeed = particle.v.length(); + ctxt.setLineWidth(field.lineWidth(particleSpeed)); + ctxt.setStrokeStyle(field.getColor(particleSpeed)); ctxt.beginPath(); - ctxt.moveTo(particle.pxOld.x, particle.pxOld.y); - particle.pxOld = projection.latlng2pixel(particle.pos); - ctxt.lineTo(particle.pxOld.x, particle.pxOld.y); + ctxt.moveTo(particle.previousPosition.x, particle.previousPosition.y); + particle.previousPosition = projection.latlng2pixel(particle.currentPosition); + ctxt.lineTo(particle.previousPosition.x, particle.previousPosition.y); ctxt.stroke(); } } + /** + * Moves each particle by its vector {@link Particle#v} multiplied by the speed which is 0.01 times the + * {@link VectorField#motionScale(int)} at the map's current zoom level. + */ private boolean execute() { double speed = 0.01 * field.motionScale(map.getZoom()); for (int idx = 0; idx < particles.length; idx++) { Particle particle = particles[idx]; - if ((particle.age > 0) && (particle.v != null)) { - double latDeg = particle.pos.getLatDeg() + speed * particle.v.y; - double lngDeg = particle.pos.getLngDeg() + speed * particle.v.x; - particle.pos = new DegreePosition(latDeg, lngDeg); - particle.speed = particle.v.length(); - particle.age--; - if ((particle.age > 0) && (this.field.inBounds(particle.pos, isVisFull()))) { - particle.v = field.getVector(particle.pos); + if ((particle.stepsToLive > 0) && (particle.v != null)) { + double latDeg = particle.currentPosition.getLatDeg() + speed * particle.v.y; + double lngDeg = particle.currentPosition.getLngDeg() + speed * particle.v.x; + particle.currentPosition = new DegreePosition(latDeg, lngDeg); + particle.stepsToLive--; + if ((particle.stepsToLive > 0) && (this.field.inBounds(particle.currentPosition, isVisFull()))) { + particle.v = field.getVector(particle.currentPosition); } else { particle.v = null; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index 34b98359620..b137fb383c8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -2,17 +2,49 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import com.sap.sailing.domain.common.Position; +/** + * A field of vectors to display in a streamlet {@link Swarm}. The {@link Vector}s returned by {@link #getVector(Position)} + * are used to initialize {@link Particle}s. Their "speed" is the {@link Vector#length() length} of the vector.

+ * + * This field provides color strings for each possible speed which are then used to style the particles as they fly through + * the swarm. + * + * @author Christopher Ronnewinkel (D036654) + * @author Axel Uhl (D043530) + * + */ public interface VectorField { + /** + * Tells whether p is within the bounds of this field. + */ boolean inBounds(Position p, boolean visFull); + /** + * The vector field's value at position p. + */ Vector getVector(Position p); + /** + * @return the maximum {@link Vector#length() length} of any vector returned by {@link #getVector(Position)} in this field for + * positions that are {@link #inBounds(Position, boolean) within bounds}. + */ double getMaxLength(); double motionScale(int zoomLevel); + /** + * A weight between 0.0 and 1.0 (inclusive) that tells the probability at which a particle at position p + * and with speed vector v will be shown. 1.0 means it will certainly be shown; 0.0 means it will certainly + * not be shown. + */ double particleWeight(Position p, Vector v); + /** + * Computes a line width for a particle flying at a certain speed. + * + * @param speed + * a speed as obtained by computing a {@link Vector}'s {@link Vector#length() length}. + */ double lineWidth(double speed); Position[] getFieldCorners(boolean visFull); @@ -25,5 +57,13 @@ public interface VectorField { void prevStep(); + /** + * Computes a color for a particle flying at a certain speed. + * + * @param speed + * a speed as obtained by computing a {@link Vector}'s {@link Vector#length() length}. + * @return an RGB color with optional transparency that is a legal color in CSS. Example: + * "rgba(1, 2, 3, 0.4)" + */ String getColor(double speed); } From df57ae0065ed18f3c51b6e4d88f4f2fd45194a99 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 01:34:14 +0200 Subject: [PATCH 013/110] update Particle in one place; more comments; added FIXME regarding VectorField.getFieldCorners() and the international date line --- .../gwt/ui/simulator/streamlets/Particle.java | 3 +- .../ui/simulator/streamlets/RectField.java | 7 +-- .../simulator/streamlets/SimulatorField.java | 41 +++++------------ .../gwt/ui/simulator/streamlets/Swarm.java | 45 +++++++------------ .../ui/simulator/streamlets/VectorField.java | 24 +++++++--- 5 files changed, 52 insertions(+), 68 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java index 541ec08b9b3..ae0b6446920 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java @@ -4,7 +4,8 @@ import com.sap.sailing.domain.common.Position; public class Particle { public Position currentPosition; - public Vector previousPosition; + public Vector previousPixelCoordinate; + public Vector currentPixelCoordinate; public int stepsToLive; public Vector v; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index 83765a2f47d..b16aa2a3266 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -92,7 +92,7 @@ public class RectField implements VectorField { } @Override - public boolean inBounds(Position p, boolean visFull) { + public boolean inBounds(Position p) { return p.getLngDeg() >= this.x0 && p.getLngDeg() < this.x1 && p.getLatDeg() >= this.y0 && p.getLatDeg() < this.y1; } @@ -140,7 +140,7 @@ public class RectField implements VectorField { @Override public double particleWeight(Position p, Vector v) { - return 1.0 - v.length() / this.maxLength; + return v == null ? 0 : (1.0 - v.length() / this.maxLength); } @Override @@ -169,7 +169,8 @@ public class RectField implements VectorField { } @Override - public Position[] getFieldCorners(boolean visFull) { + public Position[] getFieldCorners() { + // FIXME this is not date line-safe. If the field crosses +180°E (the international date line), this will fail DegreePosition[] result = new DegreePosition[2]; result[0] = new DegreePosition(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); result[1] = new DegreePosition(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 07e9504c962..6cb6fc54b0d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -104,19 +104,19 @@ public class SimulatorField implements VectorField { gvX.getLngDeg() * (this.resX + 2 * this.borderX - 1) / (this.resX - 1)); } - private Position getInnerPosition(double factX, double factY, boolean visFull) { + private Position getInnerPosition(double factX, double factY) { double latDeg = this.bdA.getLatDeg() + factY * this.bdB.getLatDeg() + factX * this.bdC.getLatDeg(); double lngDeg = this.bdA.getLngDeg() + factY * this.bdB.getLngDeg() + factX * this.bdC.getLngDeg(); Position result = new DegreePosition(latDeg, lngDeg); - if (swarmDebug && (!this.inBounds(result, visFull))) { + if (swarmDebug && (!this.inBounds(result))) { GWT.log("random-position: out of bounds"); } return result; } @Override - public boolean inBounds(Position p, boolean visFull) { - Index idx = this.getIndex(p, visFull); + public boolean inBounds(Position p) { + Index idx = this.getIndex(p); boolean inBool = (idx.x >= 0) && (idx.x < (this.resX + 2 * this.borderX)) && (idx.y >= 0) && (idx.y < (this.resY + 2 * this.borderY)); return inBool; @@ -170,27 +170,13 @@ public class SimulatorField implements VectorField { return this.interpolate(p); } - private Index getIndex(Position p, boolean visFull) { + private Index getIndex(Position p) { // calculate grid indexes final double latOffset = p.getLatDeg() - this.rcStart.getLatDeg(); final double lngOffset = (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale; // closest grid point long yIdx = Math.round(latOffset * this.nvY.getLatDeg() + lngOffset * this.nvY.getLngDeg()) + this.borderY; long xIdx = Math.round(latOffset * this.nvX.getLatDeg() + lngOffset * this.nvX.getLngDeg() + (this.resX - 1) / 2.) + this.borderX; - if (visFull) { - if (yIdx >= (this.resY + 2 * this.borderY)) { - yIdx = this.resY + 2 * this.borderY - 1; - } - if (yIdx < 0) { - yIdx = 0; - } - if (xIdx >= (this.resX + 2 * this.borderX)) { - xIdx = this.resX + 2 * this.borderX - 1; - } - if (xIdx < 0) { - xIdx = 0; - } - } return new Index(xIdx, yIdx); } @@ -244,7 +230,7 @@ public class SimulatorField implements VectorField { @Override public double particleWeight(Position p, Vector v) { - return v.length() / this.maxLength + 0.1; + return v == null ? 0 : (v.length() / this.maxLength + 0.1); } private String[] createColorsForSpeeds() { @@ -289,26 +275,23 @@ public class SimulatorField implements VectorField { } @Override - public Position[] getFieldCorners(boolean visFull) { - Position fieldNE = this.getInnerPosition(+0.5, 1.0, visFull); - Position fieldSW = this.getInnerPosition(-0.5, 0.0, visFull); - Position fieldSE = this.getInnerPosition(+0.5, 0.0, visFull); - Position fieldNW = this.getInnerPosition(-0.5, 1.0, visFull); - + public Position[] getFieldCorners() { + // FIXME this is not date line-safe. If the field crosses +180°E (the international date line), this will fail + Position fieldNE = this.getInnerPosition(+0.5, 1.0); + Position fieldSW = this.getInnerPosition(-0.5, 0.0); + Position fieldSE = this.getInnerPosition(+0.5, 0.0); + Position fieldNW = this.getInnerPosition(-0.5, 1.0); DegreePosition[] result = new DegreePosition[2]; - double minLat = Math.min(Math.min(fieldNE.getLatDeg(), fieldSW.getLatDeg()), Math.min(fieldNW.getLatDeg(), fieldSE.getLatDeg())); double minLng = Math.min(Math.min(fieldNE.getLngDeg(), fieldSW.getLngDeg()), Math.min(fieldNW.getLngDeg(), fieldSE.getLngDeg())); result[0] = new DegreePosition(minLat, minLng); - double maxLat = Math.max(Math.max(fieldNE.getLatDeg(), fieldSW.getLatDeg()), Math.max(fieldNW.getLatDeg(), fieldSE.getLatDeg())); double maxLng = Math.max(Math.max(fieldNE.getLngDeg(), fieldSW.getLngDeg()), Math.max(fieldNW.getLngDeg(), fieldSE.getLngDeg())); result[1] = new DegreePosition(maxLat, maxLng); - return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 84c90a425d0..ae3dcf3d078 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -28,7 +28,6 @@ public class Swarm { private int swarmPause = 0; private boolean swarmContinue = true; private Position visNE; - private boolean visFull; private Position visSW; public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { @@ -74,12 +73,12 @@ public class Swarm { Vector v = field.getVector(particle.currentPosition); double weight = field.particleWeight(particle.currentPosition, v); if (weight >= Math.random()) { - if (v.length() == 0) { + if (v == null || v.length() == 0) { particle.stepsToLive = 0; } else { particle.stepsToLive = 1 + (int) Math.round(Math.random() * 40); } - particle.previousPosition = projection.latlng2pixel(particle.currentPosition); + particle.currentPixelCoordinate = projection.latlng2pixel(particle.currentPosition); particle.v = v; done = true; } @@ -89,25 +88,14 @@ public class Swarm { private Position getRandomPosition() { final Position result; - if (isVisFull()) { - double rndY = Math.random(); - double rndX = Math.random(); - double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); - double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); - result = new DegreePosition(latDeg, lngDeg); - } else { - double rndY = Math.random(); - double rndX = Math.random() - 0.5; - result = field.getInnerPosition(rndX, rndY, isVisFull()); - } + double rndY = Math.random(); + double rndX = Math.random(); + double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); + double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); + result = new DegreePosition(latDeg, lngDeg); return result; } - - private boolean isVisFull() { - return visFull; - } - private Particle[] createParticles() { Particle[] newParticles = new Particle[nParticles]; for (int idx = 0; idx < newParticles.length; idx++) { @@ -126,7 +114,7 @@ public class Swarm { .getNorthEast().getLongitude()); Position mapSW = new DegreePosition(map.getBounds().getSouthWest().getLatitude(), map.getBounds() .getSouthWest().getLongitude()); - Position[] fieldCorners = this.field.getFieldCorners(visFull); + Position[] fieldCorners = this.field.getFieldCorners(); Position fieldNE = fieldCorners[1]; Position fieldSW = fieldCorners[0]; Pair visibleNE = this.isVisible(fieldNE); @@ -163,7 +151,6 @@ public class Swarm { } this.setVisSW(boundsSW); this.setVisNE(boundsNE); - this.setVisFullCanvas((!useBoundsNorth) && (!useBoundsEast) && (!useBoundsSouth) && (!useBoundsWest)); Vector boundsSWpx = this.projection.latlng2pixel(boundsSW); Vector boundsNEpx = this.projection.latlng2pixel(boundsNE); double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); @@ -171,10 +158,6 @@ public class Swarm { this.nParticles = (int) Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); }; - private void setVisFullCanvas(boolean visFull) { - this.visFull = visFull; - } - private void setVisNE(Position visNE) { this.visNE = visNE; } @@ -240,9 +223,8 @@ public class Swarm { ctxt.setLineWidth(field.lineWidth(particleSpeed)); ctxt.setStrokeStyle(field.getColor(particleSpeed)); ctxt.beginPath(); - ctxt.moveTo(particle.previousPosition.x, particle.previousPosition.y); - particle.previousPosition = projection.latlng2pixel(particle.currentPosition); - ctxt.lineTo(particle.previousPosition.x, particle.previousPosition.y); + ctxt.moveTo(particle.previousPixelCoordinate.x, particle.previousPixelCoordinate.y); + ctxt.lineTo(particle.currentPixelCoordinate.x, particle.currentPixelCoordinate.y); ctxt.stroke(); } } @@ -256,11 +238,16 @@ public class Swarm { for (int idx = 0; idx < particles.length; idx++) { Particle particle = particles[idx]; if ((particle.stepsToLive > 0) && (particle.v != null)) { + // move the particle one step in the direction and with the speed indicated by particle.v and + // update its currentPosition, currentPixelCoordinate and previousPixelCoordinate fields; + // also, its particle.v field is updated based on its new position from the vector field + particle.previousPixelCoordinate = particle.currentPixelCoordinate; double latDeg = particle.currentPosition.getLatDeg() + speed * particle.v.y; double lngDeg = particle.currentPosition.getLngDeg() + speed * particle.v.x; particle.currentPosition = new DegreePosition(latDeg, lngDeg); + particle.currentPixelCoordinate = projection.latlng2pixel(particle.currentPosition); particle.stepsToLive--; - if ((particle.stepsToLive > 0) && (this.field.inBounds(particle.currentPosition, isVisFull()))) { + if ((particle.stepsToLive > 0) && (this.field.inBounds(particle.currentPosition))) { particle.v = field.getVector(particle.currentPosition); } else { particle.v = null; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index b137fb383c8..2762a305191 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -17,25 +17,37 @@ public interface VectorField { /** * Tells whether p is within the bounds of this field. */ - boolean inBounds(Position p, boolean visFull); + boolean inBounds(Position p); /** * The vector field's value at position p. + * + * @return the speed/direction vector that tells how a particle will fly at this position in the vector field, or + * null if there should not be a flying particle, e.g., because the field does not know how a + * particle would fly at this position. An implementation that does not cleverly extrapolate outside the + * field's {@link #inBounds(Position) bounds} should return null for out-of-bounds + * positions. */ Vector getVector(Position p); /** * @return the maximum {@link Vector#length() length} of any vector returned by {@link #getVector(Position)} in this field for - * positions that are {@link #inBounds(Position, boolean) within bounds}. + * positions that are {@link #inBounds(Position) within bounds}. */ double getMaxLength(); double motionScale(int zoomLevel); /** - * A weight between 0.0 and 1.0 (inclusive) that tells the probability at which a particle at position p - * and with speed vector v will be shown. 1.0 means it will certainly be shown; 0.0 means it will certainly - * not be shown. + * A weight between 0.0 and 1.0 (inclusive) that tells the probability at which a particle at position + * p and with speed vector v will be shown. 1.0 means it will certainly be shown; 0.0 + * means it will certainly not be shown. + * + * @param p + * the particle's position + * @param v + * the particle's speed/direction vector which may be null; in this case, an implementation + * should return 0.0 as the particle's weight. */ double particleWeight(Position p, Vector v); @@ -47,7 +59,7 @@ public interface VectorField { */ double lineWidth(double speed); - Position[] getFieldCorners(boolean visFull); + Position[] getFieldCorners(); double getParticleFactor(); From 729415474ccc4fec9ecdddf81f039e9231be600d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 02:16:36 +0200 Subject: [PATCH 014/110] added bounds with intersects implementation --- .../com/sap/sailing/domain/common/Bounds.java | 12 +++ .../domain/common/impl/BoundsImpl.java | 71 +++++++++++++++++ .../sap/sailing/domain/test/BoundsTest.java | 34 ++++++++ .../gwt/ui/simulator/streamlets/Swarm.java | 79 +++++++++---------- .../ui/simulator/streamlets/VectorField.java | 6 ++ 5 files changed, 160 insertions(+), 42 deletions(-) create mode 100755 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java create mode 100755 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java create mode 100755 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java new file mode 100755 index 00000000000..b33526ff397 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java @@ -0,0 +1,12 @@ +package com.sap.sailing.domain.common; + +public interface Bounds { + Position getNorthEast(); + Position getSouthWest(); + Bounds intersect(Bounds other); + Bounds union(Bounds other); + boolean intersects(Bounds other); + boolean contains(Position other); + boolean contains(Bounds other); + boolean isCrossesDateLine(); +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java new file mode 100755 index 00000000000..1ff89986897 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -0,0 +1,71 @@ +package com.sap.sailing.domain.common.impl; + +import com.sap.sailing.domain.common.Bounds; +import com.sap.sailing.domain.common.Position; + +public class BoundsImpl implements Bounds { + private final Position sw; + private final Position ne; + private final boolean crossesDateLine; + + public BoundsImpl(Position sw, Position ne) { + super(); + this.sw = sw; + this.ne = ne; + crossesDateLine = ne.getLngDeg() < sw.getLngDeg(); + } + + @Override + public Position getNorthEast() { + return ne; + } + + @Override + public Position getSouthWest() { + return sw; + } + + @Override + public Bounds intersect(Bounds other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bounds union(Bounds other) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean intersects(Bounds other) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean contains(Position other) { + return + // lat contained: + other.getLatDeg() >= getSouthWest().getLatDeg() && other.getLatDeg() <= getNorthEast().getLatDeg() && + // lng contained: + (isCrossesDateLine() + // between SW and date line... + ? (other.getLngDeg() <= 180 && other.getLngDeg() >= getSouthWest().getLngDeg()) || + // ...or between date line and NE + (other.getLngDeg() >= -180 && other.getLngDeg() <= getNorthEast().getLngDeg()) + // these bounds are not crossing the date line; simple numeric comparison + : other.getLngDeg() >= getSouthWest().getLngDeg() && other.getLngDeg() <= getNorthEast().getLngDeg()) + ; + } + + @Override + public boolean contains(Bounds other) { + return isCrossesDateLine() == other.isCrossesDateLine() && contains(other.getNorthEast()) && contains(other.getSouthWest()); + } + + @Override + public boolean isCrossesDateLine() { + return crossesDateLine; + } +} diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java new file mode 100755 index 00000000000..c511132eabc --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -0,0 +1,34 @@ +package com.sap.sailing.domain.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.sap.sailing.domain.common.Bounds; +import com.sap.sailing.domain.common.impl.BoundsImpl; +import com.sap.sailing.domain.common.impl.DegreePosition; + +public class BoundsTest { + @Test + public void simpleContains() { + Bounds b = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + assertTrue(b.contains(new DegreePosition(2, 2))); + assertFalse(b.contains(new DegreePosition(4, 4))); + assertFalse(b.contains(new DegreePosition(0, 0))); + assertFalse(b.contains(new DegreePosition(0, 2))); + assertFalse(b.contains(new DegreePosition(2, 0))); + assertFalse(b.contains(new DegreePosition(2, 4))); + } + + @Test + public void crossDateLineContains() { + Bounds b = new BoundsImpl(new DegreePosition(1, 179), new DegreePosition(3, -179)); + assertTrue(b.contains(new DegreePosition(2, 180))); + assertFalse(b.contains(new DegreePosition(4, -178))); + assertFalse(b.contains(new DegreePosition(0, 178))); + assertFalse(b.contains(new DegreePosition(0, 180))); + assertFalse(b.contains(new DegreePosition(2, 178))); + assertFalse(b.contains(new DegreePosition(2, -178))); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index ae3dcf3d078..d3ab638bb1d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -6,6 +6,8 @@ import com.google.gwt.canvas.client.Canvas; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.base.LatLng; +import com.google.gwt.maps.client.base.LatLngBounds; import com.google.gwt.user.client.Timer; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; @@ -22,13 +24,29 @@ public class Swarm { private Timer loopTimer; private Mercator projection; private VectorField field; + + /** + * The number of particles to show. After {@link #updateBounds()} has been run, this also reflects the size of the + * {@link #particles} array. + */ private int nParticles; + + /** + * The particles shown in this swarm. + */ private Particle[] particles; + + /** + * Tells if nothing of this swarm is currently visible on the {@link #map}. This is the case when there is no + * intersection between the {@link #field vector field's} {@link VectorField#getFieldCorners() bounds} and the + * visible map area. + */ private boolean swarmOffScreen = false; + private int swarmPause = 0; private boolean swarmContinue = true; - private Position visNE; - private Position visSW; + private Position visiblePartOfFieldNE; + private Position visiblePartOfFieldSW; public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { this.fullcanvas = fullcanvas; @@ -90,8 +108,8 @@ public class Swarm { final Position result; double rndY = Math.random(); double rndX = Math.random(); - double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); - double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); + double latDeg = rndY * this.visiblePartOfFieldSW.getLatDeg() + (1 - rndY) * this.visiblePartOfFieldNE.getLatDeg(); + double lngDeg = rndX * this.visiblePartOfFieldSW.getLngDeg() + (1 - rndX) * this.visiblePartOfFieldNE.getLngDeg(); result = new DegreePosition(latDeg, lngDeg); return result; } @@ -117,40 +135,13 @@ public class Swarm { Position[] fieldCorners = this.field.getFieldCorners(); Position fieldNE = fieldCorners[1]; Position fieldSW = fieldCorners[0]; - Pair visibleNE = this.isVisible(fieldNE); - Pair visibleSW = this.isVisible(fieldSW); - boolean useBoundsNorth = visibleNE.getB(); - boolean useBoundsEast = visibleNE.getA(); - boolean useBoundsSouth = visibleSW.getB(); - boolean useBoundsWest = visibleSW.getA(); - swarmOffScreen = !visibleNE.getB() || !visibleNE.getA(); - final Position boundsNE; - final Position boundsSW; - if (swarmOffScreen) { - boundsNE = fieldNE; - boundsSW = fieldSW; - } else { - if ((!useBoundsNorth) && (!useBoundsEast)) { - boundsNE = mapNE; - } else if (!useBoundsNorth) { - boundsNE = new DegreePosition(mapNE.getLatDeg(), fieldNE.getLngDeg()); - } else if (!useBoundsEast) { - boundsNE = new DegreePosition(fieldNE.getLatDeg(), mapNE.getLngDeg()); - } else { - boundsNE = fieldNE; - } - if ((!useBoundsSouth) && (!useBoundsWest)) { - boundsSW = mapSW; - } else if (!useBoundsSouth) { - boundsSW = new DegreePosition(mapSW.getLatDeg(), fieldSW.getLngDeg()); - } else if (!useBoundsWest) { - boundsSW = new DegreePosition(fieldSW.getLatDeg(), mapSW.getLngDeg()); - } else { - boundsSW = fieldSW; - } - } - this.setVisSW(boundsSW); - this.setVisNE(boundsNE); + LatLngBounds fieldBounds = LatLngBounds.newInstance( + LatLng.newInstance(fieldSW.getLatDeg(), fieldSW.getLngDeg()), + LatLng.newInstance(fieldNE.getLatDeg(), fieldNE.getLngDeg())); + swarmOffScreen = !map.getBounds().intersects(fieldBounds); + LatLngBounds visibleBoundsOfField = intersect(fieldBounds, map.getBounds()); + this.setVisiblePartOfFieldSW(visibleBoundsOfField.getSouthWest()); + this.setVisiblePartOfFieldNE(visibleBoundsOfField.getNorthEast()); Vector boundsSWpx = this.projection.latlng2pixel(boundsSW); Vector boundsNEpx = this.projection.latlng2pixel(boundsNE); double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); @@ -158,12 +149,16 @@ public class Swarm { this.nParticles = (int) Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); }; - private void setVisNE(Position visNE) { - this.visNE = visNE; + private LatLngBounds intersect(LatLngBounds fieldBounds, LatLngBounds mapBounds) { + return LatLngBounds.newInstance(fieldBounds.contains(mapBounds.getSouthWest()) ? mapBounds.getSouthWest() : null, ne); } - private void setVisSW(Position visSW) { - this.visSW = visSW; + private void setVisiblePartOfFieldNE(Position visNE) { + this.visiblePartOfFieldNE = visNE; + } + + private void setVisiblePartOfFieldSW(Position visSW) { + this.visiblePartOfFieldSW = visSW; } /** diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index 2762a305191..5e5857fac2f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -59,6 +59,12 @@ public interface VectorField { */ double lineWidth(double speed); + /** + * @return the north-east and the south-west corner of the rectangular area on a Mercator projection that fully + * contains this field's {@link #inBounds(Position) bounds}. Note that the field's + * {@link #inBounds(Position) bounds} are not necessarily defined as a rectangle on a Mercator map. There + * may be a rotation or some other shape in place. + */ Position[] getFieldCorners(); double getParticleFactor(); From 43b9d5565705b01377cc117e0e74e37f5b85609f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 02:30:28 +0200 Subject: [PATCH 015/110] fixed TimePoint/Duration stuff for step definition in simulator package --- .../ui/simulator/streamlets/RectField.java | 11 ++-- .../simulator/streamlets/SimulatorField.java | 11 ++-- .../gwt/ui/simulator/streamlets/Swarm.java | 59 ++++--------------- .../ui/simulator/streamlets/VectorField.java | 3 +- .../sailing/simulator/test/SimulatorTest.java | 4 +- .../sailing/simulator/test/TreeGrowTest.java | 4 +- .../test/WindFieldGeneratorTest.java | 8 +-- .../windfield/WindFieldGenerator.java | 2 +- .../impl/WindFieldGeneratorBlastImpl.java | 5 +- .../impl/WindFieldGeneratorCombined.java | 3 +- .../impl/WindFieldGeneratorImpl.java | 7 ++- .../impl/WindFieldGeneratorMeasured.java | 3 +- .../WindFieldGeneratorOscillationImpl.java | 3 +- 13 files changed, 51 insertions(+), 72 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index b16aa2a3266..6e9562c88b3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -3,7 +3,9 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.maps.client.base.LatLng; +import com.sap.sailing.domain.common.Bounds; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.BoundsImpl; import com.sap.sailing.domain.common.impl.DegreePosition; public class RectField implements VectorField { @@ -169,12 +171,11 @@ public class RectField implements VectorField { } @Override - public Position[] getFieldCorners() { + public Bounds getFieldCorners() { // FIXME this is not date line-safe. If the field crosses +180°E (the international date line), this will fail - DegreePosition[] result = new DegreePosition[2]; - result[0] = new DegreePosition(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); - result[1] = new DegreePosition(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); - return result; + Position sw = new DegreePosition(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + Position ne = new DegreePosition(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + return new BoundsImpl(sw, ne); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 6cb6fc54b0d..8046055096d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -3,7 +3,9 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import java.util.List; import com.google.gwt.core.client.GWT; +import com.sap.sailing.domain.common.Bounds; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.BoundsImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; @@ -275,24 +277,23 @@ public class SimulatorField implements VectorField { } @Override - public Position[] getFieldCorners() { + public Bounds getFieldCorners() { // FIXME this is not date line-safe. If the field crosses +180°E (the international date line), this will fail Position fieldNE = this.getInnerPosition(+0.5, 1.0); Position fieldSW = this.getInnerPosition(-0.5, 0.0); Position fieldSE = this.getInnerPosition(+0.5, 0.0); Position fieldNW = this.getInnerPosition(-0.5, 1.0); - DegreePosition[] result = new DegreePosition[2]; double minLat = Math.min(Math.min(fieldNE.getLatDeg(), fieldSW.getLatDeg()), Math.min(fieldNW.getLatDeg(), fieldSE.getLatDeg())); double minLng = Math.min(Math.min(fieldNE.getLngDeg(), fieldSW.getLngDeg()), Math.min(fieldNW.getLngDeg(), fieldSE.getLngDeg())); - result[0] = new DegreePosition(minLat, minLng); + Position sw = new DegreePosition(minLat, minLng); double maxLat = Math.max(Math.max(fieldNE.getLatDeg(), fieldSW.getLatDeg()), Math.max(fieldNW.getLatDeg(), fieldSE.getLatDeg())); double maxLng = Math.max(Math.max(fieldNE.getLngDeg(), fieldSW.getLngDeg()), Math.max(fieldNW.getLngDeg(), fieldSE.getLngDeg())); - result[1] = new DegreePosition(maxLat, maxLng); - return result; + Position ne = new DegreePosition(maxLat, maxLng); + return new BoundsImpl(sw, ne); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index d3ab638bb1d..cc9a8689b91 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -6,15 +6,14 @@ import com.google.gwt.canvas.client.Canvas; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; -import com.google.gwt.maps.client.base.LatLng; -import com.google.gwt.maps.client.base.LatLngBounds; import com.google.gwt.user.client.Timer; +import com.sap.sailing.domain.common.Bounds; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.BoundsImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; -import com.sap.sse.common.Util.Pair; public class Swarm { private final FullCanvasOverlay fullcanvas; @@ -45,8 +44,7 @@ public class Swarm { private int swarmPause = 0; private boolean swarmContinue = true; - private Position visiblePartOfFieldNE; - private Position visiblePartOfFieldSW; + private Bounds visibleBoundsOfField; public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { this.fullcanvas = fullcanvas; @@ -108,8 +106,8 @@ public class Swarm { final Position result; double rndY = Math.random(); double rndX = Math.random(); - double latDeg = rndY * this.visiblePartOfFieldSW.getLatDeg() + (1 - rndY) * this.visiblePartOfFieldNE.getLatDeg(); - double lngDeg = rndX * this.visiblePartOfFieldSW.getLngDeg() + (1 - rndX) * this.visiblePartOfFieldNE.getLngDeg(); + double latDeg = rndY * this.visibleBoundsOfField.getSouthWest().getLatDeg() + (1 - rndY) * this.visibleBoundsOfField.getNorthEast().getLatDeg(); + double lngDeg = rndX * this.visibleBoundsOfField.getSouthWest().getLngDeg() + (1 - rndX) * this.visibleBoundsOfField.getNorthEast().getLngDeg(); result = new DegreePosition(latDeg, lngDeg); return result; } @@ -128,50 +126,19 @@ public class Swarm { } private void updateBounds() { - Position mapNE = new DegreePosition(map.getBounds().getNorthEast().getLatitude(), map.getBounds() - .getNorthEast().getLongitude()); - Position mapSW = new DegreePosition(map.getBounds().getSouthWest().getLatitude(), map.getBounds() - .getSouthWest().getLongitude()); - Position[] fieldCorners = this.field.getFieldCorners(); - Position fieldNE = fieldCorners[1]; - Position fieldSW = fieldCorners[0]; - LatLngBounds fieldBounds = LatLngBounds.newInstance( - LatLng.newInstance(fieldSW.getLatDeg(), fieldSW.getLngDeg()), - LatLng.newInstance(fieldNE.getLatDeg(), fieldNE.getLngDeg())); - swarmOffScreen = !map.getBounds().intersects(fieldBounds); - LatLngBounds visibleBoundsOfField = intersect(fieldBounds, map.getBounds()); - this.setVisiblePartOfFieldSW(visibleBoundsOfField.getSouthWest()); - this.setVisiblePartOfFieldNE(visibleBoundsOfField.getNorthEast()); - Vector boundsSWpx = this.projection.latlng2pixel(boundsSW); - Vector boundsNEpx = this.projection.latlng2pixel(boundsNE); + Bounds fieldBounds = this.field.getFieldCorners(); + final BoundsImpl mapBounds = new BoundsImpl( + new DegreePosition(map.getBounds().getSouthWest().getLatitude(), map.getBounds().getSouthWest().getLongitude()), + new DegreePosition(map.getBounds().getNorthEast().getLatitude(), map.getBounds().getNorthEast().getLongitude())); + swarmOffScreen = !fieldBounds.intersects(mapBounds); + visibleBoundsOfField = fieldBounds.intersect(mapBounds); + Vector boundsSWpx = this.projection.latlng2pixel(visibleBoundsOfField.getSouthWest()); + Vector boundsNEpx = this.projection.latlng2pixel(visibleBoundsOfField.getNorthEast()); double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); this.nParticles = (int) Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); }; - private LatLngBounds intersect(LatLngBounds fieldBounds, LatLngBounds mapBounds) { - return LatLngBounds.newInstance(fieldBounds.contains(mapBounds.getSouthWest()) ? mapBounds.getSouthWest() : null, ne); - } - - private void setVisiblePartOfFieldNE(Position visNE) { - this.visiblePartOfFieldNE = visNE; - } - - private void setVisiblePartOfFieldSW(Position visSW) { - this.visiblePartOfFieldSW = visSW; - } - - /** - * Tells if pos is within the bounds of {@link #canvas}, for X and Y coordinate. - */ - private Pair isVisible(Position pos) { - // test for visibility of swarm - Vector proj = this.projection.latlng2pixel(pos); - final boolean xVisible = proj.x >= 0 && proj.x <= canvas.getOffsetWidth(); - final boolean yVisible = proj.y >= 0 && proj.y <= canvas.getOffsetHeight(); - return new Pair(xVisible, yVisible); - } - private void startLoop(final int animationIntervalMillis) { // Create animation-loop based on timer timeout loopTimer = new com.google.gwt.user.client.Timer() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index 5e5857fac2f..4482ec8097d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; +import com.sap.sailing.domain.common.Bounds; import com.sap.sailing.domain.common.Position; /** @@ -65,7 +66,7 @@ public interface VectorField { * {@link #inBounds(Position) bounds} are not necessarily defined as a rectangle on a Mercator map. There * may be a rotation or some other shape in place. */ - Position[] getFieldCorners(); + Bounds getFieldCorners(); double getParticleFactor(); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java index dc2a5a1ace5..a29bb6039c2 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java @@ -10,9 +10,11 @@ import junit.framework.Assert; import org.junit.Test; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; @@ -49,7 +51,7 @@ public class SimulatorTest { wf.setPositionGrid(positions); Date startDate = new Date(0); TimePoint startTime = new MillisecondsTimePoint(startDate.getTime()); - TimePoint timeStep = new MillisecondsTimePoint(30000); + Duration timeStep = new MillisecondsDurationImpl(30000); wf.generate(startTime, null, timeStep); SimulationParameters param = new SimulationParametersImpl(course, pd, wf, SailingSimulatorConstants.ModeFreestyle, true, true); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java index 210645f98a2..9070c7b8565 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java @@ -8,12 +8,14 @@ import junit.framework.Assert; import org.junit.Test; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; @@ -51,7 +53,7 @@ public class TreeGrowTest { wf.setPositionGrid(positions); Date startDate = new Date(0); TimePoint startTime = new MillisecondsTimePoint(startDate.getTime()); - TimePoint timeStep = new MillisecondsTimePoint(20000); + Duration timeStep = new MillisecondsDurationImpl(20000); wf.generate(startTime, null, timeStep); SimulationParameters param = new SimulationParametersImpl(course, pd, wf, SailingSimulatorConstants.ModeFreestyle, true, true); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java index 23d2d19eb8e..742748b1292 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java @@ -14,11 +14,13 @@ import java.util.logging.Logger; import org.junit.Test; import com.sap.sailing.domain.base.impl.KilometersPerHourSpeedWithBearingImpl; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.simulator.impl.RectangularBoundary; @@ -98,9 +100,7 @@ public class WindFieldGeneratorTest { wf.setPositionGrid(bd.extractGrid(hSteps, vSteps, 0, 0)); Position[][] positionGrid = wf.getPositionGrid(); TimePoint startTime = new MillisecondsTimePoint(0); - TimePoint timeStep = new MillisecondsTimePoint(30 * 1000); - - + Duration timeStep = new MillisecondsDurationImpl(30 * 1000); wf.generate(startTime, null, timeStep); wf.setTimeScale(1.0); SpeedWithBearing speed = new KilometersPerHourSpeedWithBearingImpl(0, new DegreeBearingImpl(0)); @@ -243,7 +243,7 @@ public class WindFieldGeneratorTest { wf.setPositionGrid(bd.extractGrid(hSteps, vSteps, 0, 0)); Position[][] positionGrid = wf.getPositionGrid(); TimePoint startTime = new MillisecondsTimePoint(0); - TimePoint timeStep = new MillisecondsTimePoint(30 * 1000); + Duration timeStep = new MillisecondsDurationImpl(30 * 1000); wf.generate(startTime, null, timeStep); SpeedWithBearing speed = new KilometersPerHourSpeedWithBearingImpl(0, new DegreeBearingImpl(0)); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java index 7aa17261023..450b417995a 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java @@ -32,7 +32,7 @@ public interface WindFieldGenerator extends WindField, Serializable { * from the start time * @return */ - public TimePoint getTimeStep(); + public Duration getTimeStep(); /** * The last TimePoint for which the wind field is generated, could be null if no diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java index 9a511330761..23d74cfffd8 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java @@ -6,6 +6,7 @@ import umontreal.iro.lecuyer.randvar.GeometricGen; import umontreal.iro.lecuyer.randvar.NormalGen; import umontreal.iro.lecuyer.rng.RandomStream; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; @@ -48,11 +49,11 @@ public class WindFieldGeneratorBlastImpl extends WindFieldGeneratorImpl implemen } @Override - public void generate(TimePoint start, TimePoint end, TimePoint step) { + public void generate(TimePoint start, TimePoint end, Duration step) { generate(start, end, step, windParameters.baseWindSpeed, windParameters.baseWindBearing); } - protected void generate(TimePoint start, TimePoint end, TimePoint step, double defaultSpeed, double defaultBearing) { + protected void generate(TimePoint start, TimePoint end, Duration step, double defaultSpeed, double defaultBearing) { super.generate(start, end, step); // TODO Check the defaults setDefaultWindSpeed(defaultSpeed); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java index 6ab2fb48f18..863891c471d 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java @@ -1,6 +1,7 @@ package com.sap.sailing.simulator.windfield.impl; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; @@ -33,7 +34,7 @@ public class WindFieldGeneratorCombined extends WindFieldGeneratorImpl implement } @Override - public void generate(TimePoint start, TimePoint end, TimePoint step) { + public void generate(TimePoint start, TimePoint end, Duration step) { super.generate(start, end, step); blastGen.setPositionGrid(positions); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java index dc8962013ab..f1d1e5ac3c7 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.logging.Logger; import com.sap.sailing.domain.base.impl.KilometersPerHourSpeedWithBearingImpl; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; @@ -41,7 +42,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { /** * TimePoint which constitutes one unit of time */ - protected TimePoint timeStep; + protected Duration timeStep; private static Logger logger = Logger.getLogger("com.sap.sailing.windfield"); @@ -155,7 +156,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { } @Override - public void generate(TimePoint start, TimePoint end, TimePoint step) { + public void generate(TimePoint start, TimePoint end, Duration step) { this.startTime = start; this.endTime = end; this.timeStep = step; @@ -167,7 +168,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { } @Override - public TimePoint getTimeStep() { + public Duration getTimeStep() { return this.timeStep; } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java index 5e394883eed..a8cdba604eb 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.logging.Logger; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; @@ -38,7 +39,7 @@ public class WindFieldGeneratorMeasured extends WindFieldGeneratorImpl implement } @Override - public void generate(TimePoint start, TimePoint end, TimePoint step) { + public void generate(TimePoint start, TimePoint end, Duration step) { super.generate(start, end, step); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java index 031c4f61923..012a949084f 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java @@ -4,6 +4,7 @@ import java.util.logging.Logger; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; @@ -36,7 +37,7 @@ public class WindFieldGeneratorOscillationImpl extends WindFieldGeneratorImpl im } @Override - public void generate(TimePoint start, TimePoint end, TimePoint step) { + public void generate(TimePoint start, TimePoint end, Duration step) { super.generate(start,end,step); if (positions == null || positions.length < 1) { return; From 45b02aa2f003f14301b728d6d6f9a0e82788c433 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 12:42:07 +0200 Subject: [PATCH 016/110] added (failing) Bounds.union tests and a first yet incorrect implementation --- .../com/sap/sailing/domain/common/Bounds.java | 2 + .../domain/common/impl/BoundsImpl.java | 27 +++++++++-- .../sap/sailing/domain/test/BoundsTest.java | 45 +++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java index b33526ff397..57f4eed7b0d 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java @@ -3,6 +3,8 @@ package com.sap.sailing.domain.common; public interface Bounds { Position getNorthEast(); Position getSouthWest(); + Position getNorthWest(); + Position getSouthEast(); Bounds intersect(Bounds other); Bounds union(Bounds other); boolean intersects(Bounds other); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java index 1ff89986897..f02873a9487 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -14,6 +14,16 @@ public class BoundsImpl implements Bounds { this.ne = ne; crossesDateLine = ne.getLngDeg() < sw.getLngDeg(); } + + @Override + public Position getNorthWest() { + return new DegreePosition(getNorthEast().getLatDeg(), getSouthWest().getLngDeg()); + } + + @Override + public Position getSouthEast() { + return new DegreePosition(getSouthWest().getLatDeg(), getNorthEast().getLngDeg()); + } @Override public Position getNorthEast() { @@ -24,7 +34,7 @@ public class BoundsImpl implements Bounds { public Position getSouthWest() { return sw; } - + @Override public Bounds intersect(Bounds other) { // TODO Auto-generated method stub @@ -33,8 +43,19 @@ public class BoundsImpl implements Bounds { @Override public Bounds union(Bounds other) { - // TODO Auto-generated method stub - return null; + final Position resultSW; + final Position resultNE; + if (contains(other.getSouthWest())) { + resultSW = getSouthWest(); + } else { + resultSW = other.getSouthWest(); + } + if (contains(other.getNorthEast())) { + resultNE = getNorthEast(); + } else { + resultNE = other.getNorthEast(); + } + return new BoundsImpl(resultSW, resultNE); } @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index c511132eabc..400f85599ea 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -1,5 +1,6 @@ package com.sap.sailing.domain.test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,4 +32,48 @@ public class BoundsTest { assertFalse(b.contains(new DegreePosition(2, 178))); assertFalse(b.contains(new DegreePosition(2, -178))); } + + @Test + public void simpleUnionTestOverlap() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, 2), new DegreePosition(4, 4)); + Bounds u = b1.union(b2); + assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001); + assertEquals(1.0, u.getSouthWest().getLngDeg(), 0.00000001); + assertEquals(4.0, u.getNorthEast().getLatDeg(), 0.00000001); + assertEquals(4.0, u.getNorthEast().getLngDeg(), 0.00000001); + } + + @Test + public void testDatelineUnionOverlap() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 179), new DegreePosition(3, -179)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, 180), new DegreePosition(4, -178)); + Bounds u = b1.union(b2); + assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001); + assertEquals(179, u.getSouthWest().getLngDeg(), 0.00000001); + assertEquals(4.0, u.getNorthEast().getLatDeg(), 0.00000001); + assertEquals(-178, u.getNorthEast().getLngDeg(), 0.00000001); + } + + @Test + public void testDatelineUnionOutside() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 179), new DegreePosition(3, -179)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, -178), new DegreePosition(5, -177)); + Bounds u = b1.union(b2); + assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001); + assertEquals(179, u.getSouthWest().getLngDeg(), 0.00000001); + assertEquals(5.0, u.getNorthEast().getLatDeg(), 0.00000001); + assertEquals(-177, u.getNorthEast().getLngDeg(), 0.00000001); + } + + @Test + public void simpleUnionTestOutside() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, 4), new DegreePosition(5, 5)); + Bounds u = b1.union(b2); + assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001); + assertEquals(1.0, u.getSouthWest().getLngDeg(), 0.00000001); + assertEquals(5.0, u.getNorthEast().getLatDeg(), 0.00000001); + assertEquals(5.0, u.getNorthEast().getLngDeg(), 0.00000001); + } } From efb0ad5462065098b570096a9d2640738c5f850e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 21:38:15 +0200 Subject: [PATCH 017/110] fixed BoundsImpl.union implementation; tests all green --- .../domain/common/impl/BoundsImpl.java | 56 +++++++++++++++---- .../sap/sailing/domain/test/BoundsTest.java | 11 ++++ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java index f02873a9487..70b887adc50 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -34,6 +34,21 @@ public class BoundsImpl implements Bounds { public Position getSouthWest() { return sw; } + + /** + * Considering whether or not this bounds object crosses the date line, calculates the width in degrees longitude + * going from west to east. The result is always a non-negative number. + */ + private double getDistanceDeg(final double west, final double east) { + final double diff = east-west; + final double result; + if (isCrossingDateLine(west, east)) { + result = 360+diff; // diff is negative in this case + } else { + result = diff; + } + return result; + } @Override public Bounds intersect(Bounds other) { @@ -43,19 +58,36 @@ public class BoundsImpl implements Bounds { @Override public Bounds union(Bounds other) { - final Position resultSW; - final Position resultNE; - if (contains(other.getSouthWest())) { - resultSW = getSouthWest(); - } else { - resultSW = other.getSouthWest(); + final double minLatDeg = Math.min(getSouthWest().getLatDeg(), other.getSouthWest().getLatDeg()); + final double maxLatDeg = Math.max(getNorthEast().getLatDeg(), other.getNorthEast().getLatDeg()); + // For lng, we can go left or right around the earth; which way results in the smaller bounds? We have four options: + final double[] west = new double[] { getSouthWest().getLngDeg(), other.getSouthWest().getLngDeg() }; + final double[] east = new double[] { getNorthEast().getLngDeg(), other.getNorthEast().getLngDeg() }; + double minLngDegDistance = Double.MAX_VALUE; + double bestWest = 0; + double bestEast = 0; + for (int w=0; w<2; w++) { + for (int e=0; e<2; e++) { + double currentLngDegDistance; + if (spansLngDeg(west[w], east[e], west[1 - w]) && spansLngDeg(west[w], east[e], east[1 - e]) + && (currentLngDegDistance = Math.abs(getDistanceDeg(west[w], east[e]))) < minLngDegDistance) { + minLngDegDistance = currentLngDegDistance; + bestWest = west[w]; + bestEast = east[e]; + } + } } - if (contains(other.getNorthEast())) { - resultNE = getNorthEast(); - } else { - resultNE = other.getNorthEast(); - } - return new BoundsImpl(resultSW, resultNE); + return new BoundsImpl(new DegreePosition(minLatDeg, bestWest), new DegreePosition(maxLatDeg, bestEast)); + } + + private boolean spansLngDeg(double westLngDeg, double eastLngDeg, double lngDeg) { + return isCrossingDateLine(westLngDeg, eastLngDeg) + ? (lngDeg >= westLngDeg && lngDeg <= 180) || (lngDeg >= -180 && lngDeg <= eastLngDeg) + : westLngDeg <= lngDeg && lngDeg <= eastLngDeg; + } + + private boolean isCrossingDateLine(double westLngDeg, double eastLngDeg) { + return westLngDeg > eastLngDeg; } @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index 400f85599ea..f082b4d74d6 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -66,6 +66,17 @@ public class BoundsTest { assertEquals(-177, u.getNorthEast().getLngDeg(), 0.00000001); } + @Test + public void testDatelineUnionOneSideAndOtherSide() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, 179)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, -178), new DegreePosition(5, -177)); + Bounds u = b1.union(b2); + assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001); + assertEquals(178, u.getSouthWest().getLngDeg(), 0.00000001); + assertEquals(5.0, u.getNorthEast().getLatDeg(), 0.00000001); + assertEquals(-177, u.getNorthEast().getLngDeg(), 0.00000001); + } + @Test public void simpleUnionTestOutside() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); From 9ca4ec4fc8a5dab22a344f80153a8af07b378bb8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 21:46:51 +0200 Subject: [PATCH 018/110] first Bounds.intersects implementation with a first set of trivial tests --- .../com/sap/sailing/domain/common/Bounds.java | 2 ++ .../domain/common/impl/BoundsImpl.java | 16 +++++++++++-- .../sap/sailing/domain/test/BoundsTest.java | 24 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java index 57f4eed7b0d..ca3fafe594d 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java @@ -11,4 +11,6 @@ public interface Bounds { boolean contains(Position other); boolean contains(Bounds other); boolean isCrossesDateLine(); + boolean containsLatDeg(double latDeg); + boolean containsLngDeg(double lngDeg); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java index 70b887adc50..3a5c9d350eb 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -92,10 +92,22 @@ public class BoundsImpl implements Bounds { @Override public boolean intersects(Bounds other) { - // TODO Auto-generated method stub - return false; + return (containsLatDeg(other.getSouthWest().getLatDeg()) || containsLatDeg(other.getNorthEast().getLatDeg()) || + other.containsLatDeg(getSouthWest().getLatDeg()) || other.containsLatDeg(getNorthEast().getLatDeg())) + && (containsLngDeg(other.getSouthWest().getLatDeg()) || containsLngDeg(other.getNorthEast().getLatDeg()) || + other.containsLngDeg(getSouthWest().getLatDeg()) || other.containsLngDeg(getNorthEast().getLatDeg())); + } + + @Override + public boolean containsLatDeg(double latDeg) { + return latDeg >= getSouthWest().getLatDeg() && latDeg <= getNorthEast().getLatDeg(); } + @Override + public boolean containsLngDeg(double lngDeg) { + return spansLngDeg(getSouthWest().getLngDeg(), getNorthEast().getLngDeg(), lngDeg); + } + @Override public boolean contains(Position other) { return diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index f082b4d74d6..37d4915f78c 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -87,4 +87,28 @@ public class BoundsTest { assertEquals(5.0, u.getNorthEast().getLatDeg(), 0.00000001); assertEquals(5.0, u.getNorthEast().getLngDeg(), 0.00000001); } + + @Test + public void simpleIntersectsTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, 2), new DegreePosition(5, 5)); + assertTrue(b1.intersects(b2)); + assertTrue(b2.intersects(b1)); + } + + @Test + public void simpleNegativeIntersectsTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, 4), new DegreePosition(5, 5)); + assertFalse(b1.intersects(b2)); + assertFalse(b2.intersects(b1)); + } + + @Test + public void intersectsWithContainsTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(4, 4)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, 2), new DegreePosition(3, 3)); + assertTrue(b1.intersects(b2)); + assertTrue(b2.intersects(b1)); + } } From fcbf94c2dd68bb916d4115cc501c107cd7dd6229 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 21:53:22 +0200 Subject: [PATCH 019/110] fixed intersects implementation, more tests --- .../domain/common/impl/BoundsImpl.java | 9 ++++- .../sap/sailing/domain/test/BoundsTest.java | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java index 3a5c9d350eb..ec8050106f6 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -94,8 +94,8 @@ public class BoundsImpl implements Bounds { public boolean intersects(Bounds other) { return (containsLatDeg(other.getSouthWest().getLatDeg()) || containsLatDeg(other.getNorthEast().getLatDeg()) || other.containsLatDeg(getSouthWest().getLatDeg()) || other.containsLatDeg(getNorthEast().getLatDeg())) - && (containsLngDeg(other.getSouthWest().getLatDeg()) || containsLngDeg(other.getNorthEast().getLatDeg()) || - other.containsLngDeg(getSouthWest().getLatDeg()) || other.containsLngDeg(getNorthEast().getLatDeg())); + && (containsLngDeg(other.getSouthWest().getLngDeg()) || containsLngDeg(other.getNorthEast().getLngDeg()) || + other.containsLngDeg(getSouthWest().getLngDeg()) || other.containsLngDeg(getNorthEast().getLngDeg())); } @Override @@ -133,4 +133,9 @@ public class BoundsImpl implements Bounds { public boolean isCrossesDateLine() { return crossesDateLine; } + + @Override + public String toString() { + return "{SW: "+getSouthWest().toString()+", NE: "+getNorthEast().toString()+"}"; + } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index 37d4915f78c..9f7da662d57 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -104,6 +104,14 @@ public class BoundsTest { assertFalse(b2.intersects(b1)); } + @Test + public void simpleNegativeIntersectsTestNonIntersectingLat() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, 2), new DegreePosition(5, 5)); + assertFalse(b1.intersects(b2)); + assertFalse(b2.intersects(b1)); + } + @Test public void intersectsWithContainsTest() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(4, 4)); @@ -111,4 +119,36 @@ public class BoundsTest { assertTrue(b1.intersects(b2)); assertTrue(b2.intersects(b1)); } + + @Test + public void dateLineIntersectsTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -177)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, -178), new DegreePosition(5, -176)); + assertTrue(b1.intersects(b2)); + assertTrue(b2.intersects(b1)); + } + + @Test + public void dateLineNegativeIntersectsTestNonMatchingLat() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -177)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, -178), new DegreePosition(5, -176)); + assertFalse(b1.intersects(b2)); + assertFalse(b2.intersects(b1)); + } + + @Test + public void dateLineNegativeIntersectsTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, 179)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, -178), new DegreePosition(5, -177)); + assertFalse(b1.intersects(b2)); + assertFalse(b2.intersects(b1)); + } + + @Test + public void dateLineNegativeIntersectsTestBoundsCrossingDateLine() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -179)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, -178), new DegreePosition(5, -177)); + assertFalse(b1.intersects(b2)); + assertFalse(b2.intersects(b1)); + } } From 22fe1fd27bbdbe63b891dbbbed423d5eba0c572e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 22:17:06 +0200 Subject: [PATCH 020/110] a first Bounds.intersect(Bounds) implementation, untested --- .../com/sap/sailing/domain/common/Bounds.java | 1 + .../domain/common/impl/BoundsImpl.java | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java index ca3fafe594d..3f83c6717e4 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java @@ -5,6 +5,7 @@ public interface Bounds { Position getSouthWest(); Position getNorthWest(); Position getSouthEast(); + Bounds intersect(Bounds other); Bounds union(Bounds other); boolean intersects(Bounds other); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java index ec8050106f6..d2c5992b239 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -52,8 +52,27 @@ public class BoundsImpl implements Bounds { @Override public Bounds intersect(Bounds other) { - // TODO Auto-generated method stub - return null; + final double maxSouthLatDeg = Math.max(getSouthWest().getLatDeg(), other.getSouthWest().getLatDeg()); + final double minNorthLatDeg = Math.min(getNorthEast().getLatDeg(), other.getNorthEast().getLatDeg()); + final double westDeg; + if (containsLngDeg(other.getSouthWest().getLngDeg())) { + westDeg = other.getSouthWest().getLngDeg(); + } else if (other.containsLngDeg(getSouthWest().getLngDeg())) { + westDeg = getSouthWest().getLngDeg(); + } else { + // no intersection + westDeg = getSouthWest().getLngDeg(); + } + final double eastDeg; + if (containsLngDeg(other.getNorthEast().getLngDeg())) { + eastDeg = other.getNorthEast().getLngDeg(); + } else if (other.containsLngDeg(getNorthEast().getLngDeg())) { + eastDeg = getNorthEast().getLngDeg(); + } else { + // no intersection + eastDeg = getSouthWest().getLngDeg(); + } + return new BoundsImpl(new DegreePosition(maxSouthLatDeg, westDeg), new DegreePosition(minNorthLatDeg, eastDeg)); } @Override From 8128c437781a32d5fe8364370e256b350f6980c0 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 22:21:47 +0200 Subject: [PATCH 021/110] first successful simple intersect test --- .../src/com/sap/sailing/domain/test/BoundsTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index 9f7da662d57..8f2c261aa45 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -96,6 +96,17 @@ public class BoundsTest { assertTrue(b2.intersects(b1)); } + @Test + public void simpleIntersectTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, 2), new DegreePosition(5, 5)); + Bounds i = b1.intersect(b2); + assertEquals(2, i.getSouthWest().getLatDeg(), 0.0000001); + assertEquals(3, i.getNorthEast().getLatDeg(), 0.0000001); + assertEquals(2, i.getSouthWest().getLngDeg(), 0.0000001); + assertEquals(3, i.getNorthEast().getLngDeg(), 0.0000001); + } + @Test public void simpleNegativeIntersectsTest() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); @@ -151,4 +162,5 @@ public class BoundsTest { assertFalse(b1.intersects(b2)); assertFalse(b2.intersects(b1)); } + } From 6c751943b83be82c2a92370067fc2d4151dcf36d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 22:32:36 +0200 Subject: [PATCH 022/110] introduced Bounds.isEmpty and use it in tests --- .../com/sap/sailing/domain/common/Bounds.java | 1 + .../sailing/domain/common/impl/BoundsImpl.java | 6 ++++++ .../com/sap/sailing/domain/test/BoundsTest.java | 16 ++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java index 3f83c6717e4..60a876a4f77 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Bounds.java @@ -14,4 +14,5 @@ public interface Bounds { boolean isCrossesDateLine(); boolean containsLatDeg(double latDeg); boolean containsLngDeg(double lngDeg); + boolean isEmpty(); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java index d2c5992b239..5ef09a1da53 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/BoundsImpl.java @@ -153,6 +153,12 @@ public class BoundsImpl implements Bounds { return crossesDateLine; } + @Override + public boolean isEmpty() { + return getSouthWest().getLatDeg() >= getNorthEast().getLatDeg() || + getSouthWest().getLngDeg() == getNorthEast().getLngDeg(); + } + @Override public String toString() { return "{SW: "+getSouthWest().toString()+", NE: "+getNorthEast().toString()+"}"; diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index 8f2c261aa45..b10a640a42c 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -115,6 +115,14 @@ public class BoundsTest { assertFalse(b2.intersects(b1)); } + @Test + public void simpleNegativeIntersectTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, 4), new DegreePosition(5, 5)); + Bounds i = b1.intersect(b2); + assertTrue(i.isEmpty()); + } + @Test public void simpleNegativeIntersectsTestNonIntersectingLat() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); @@ -123,6 +131,14 @@ public class BoundsTest { assertFalse(b2.intersects(b1)); } + @Test + public void simpleNegativeIntersectTestNonIntersectingLat() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, 2), new DegreePosition(5, 5)); + Bounds i = b1.intersect(b2); + assertTrue(i.isEmpty()); + } + @Test public void intersectsWithContainsTest() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(4, 4)); From 4bd2995d156f8604418a53f78f89d1ec231f099d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 22:43:18 +0200 Subject: [PATCH 023/110] copied all intersects tests into intersect tests; all green --- .../sap/sailing/domain/test/BoundsTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java index b10a640a42c..d89ba792d8c 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/BoundsTest.java @@ -147,6 +147,17 @@ public class BoundsTest { assertTrue(b2.intersects(b1)); } + @Test + public void intersectWithContainsTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(4, 4)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, 2), new DegreePosition(3, 3)); + Bounds i = b1.intersect(b2); + assertEquals(2, i.getSouthWest().getLatDeg(), 0.0000001); + assertEquals(3, i.getNorthEast().getLatDeg(), 0.0000001); + assertEquals(2, i.getSouthWest().getLngDeg(), 0.0000001); + assertEquals(3, i.getNorthEast().getLngDeg(), 0.0000001); + } + @Test public void dateLineIntersectsTest() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -177)); @@ -155,6 +166,17 @@ public class BoundsTest { assertTrue(b2.intersects(b1)); } + @Test + public void dateLineIntersectTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -177)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, -178), new DegreePosition(5, -176)); + Bounds i = b1.intersect(b2); + assertEquals(2, i.getSouthWest().getLatDeg(), 0.0000001); + assertEquals(3, i.getNorthEast().getLatDeg(), 0.0000001); + assertEquals(-178, i.getSouthWest().getLngDeg(), 0.0000001); + assertEquals(-177, i.getNorthEast().getLngDeg(), 0.0000001); + } + @Test public void dateLineNegativeIntersectsTestNonMatchingLat() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -177)); @@ -163,6 +185,14 @@ public class BoundsTest { assertFalse(b2.intersects(b1)); } + @Test + public void dateLineNegativeIntersectTestNonMatchingLat() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -177)); + Bounds b2 = new BoundsImpl(new DegreePosition(4, -178), new DegreePosition(5, -176)); + Bounds i = b1.intersect(b2); + assertTrue(i.isEmpty()); + } + @Test public void dateLineNegativeIntersectsTest() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, 179)); @@ -171,6 +201,14 @@ public class BoundsTest { assertFalse(b2.intersects(b1)); } + @Test + public void dateLineNegativeIntersectTest() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, 179)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, -178), new DegreePosition(5, -177)); + Bounds i = b1.intersect(b2); + assertTrue(i.isEmpty()); + } + @Test public void dateLineNegativeIntersectsTestBoundsCrossingDateLine() { Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -179)); @@ -179,4 +217,12 @@ public class BoundsTest { assertFalse(b2.intersects(b1)); } + @Test + public void dateLineNegativeIntersectTestBoundsCrossingDateLine() { + Bounds b1 = new BoundsImpl(new DegreePosition(1, 178), new DegreePosition(3, -179)); + Bounds b2 = new BoundsImpl(new DegreePosition(2, -178), new DegreePosition(5, -177)); + Bounds i = b1.intersect(b2); + assertTrue(i.isEmpty()); + } + } From 724d6054cd3a2757ae81c6983b688c1d0f9a3ad2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 23:40:53 +0200 Subject: [PATCH 024/110] made WindData IsSerializable --- .../main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java index a7e39689ac0..d97a0f37c5a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/WindFieldDTO.java @@ -10,7 +10,7 @@ public class WindFieldDTO implements IsSerializable { public double curSpeed; public double curBearing; - public static class WindData { + public static class WindData implements IsSerializable { public PositionDTO rcStart; public PositionDTO rcEnd; public int resX; From 050a149159ba72570617f6a74bdf5776667a2193 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jun 2014 23:51:56 +0200 Subject: [PATCH 025/110] formatting --- .../WindStreamletsCanvasOverlay.java | 2 +- .../simulator/racemap/FullCanvasOverlay.java | 44 ++++--------------- .../gwt/ui/simulator/streamlets/Swarm.java | 1 + 3 files changed, 11 insertions(+), 36 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 95c9c16717c..3a9b61b2c5a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -9,7 +9,7 @@ import com.sap.sailing.gwt.ui.simulator.streamlets.Swarm; import com.sap.sse.gwt.client.player.Timer; /** - * A google map overlay based on a HTML5 canvas for drawing a wind field. The overlay covers the whole map and displays + * A Google Maps overlay based on an HTML5 canvas for drawing a wind field. The overlay covers the whole map and displays * the wind objects inside it. * * @author Nidhi Sawhney (D054070) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java index e1ff06a09ef..c02d2d098e9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java @@ -37,23 +37,12 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi public FullCanvasOverlay(MapWidget map, int zIndex) { super(map, zIndex); - - /*getMap().addDragEndHandler( new DragEndMapHandler() { - @Override - public void onEvent(DragEndMapEvent event) { - // TODO Auto-generated method stub - draw(); - }; - });*/ - getMap().addCenterChangeHandler(new CenterChangeMapHandler() { - @Override - public void onEvent(CenterChangeMapEvent event) { - // TODO Auto-generated method stub - drawCenterChanged(); - }; + @Override + public void onEvent(CenterChangeMapEvent event) { + drawCenterChanged(); + }; }); - } /** @@ -157,11 +146,11 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi drawCircle(x, y,radius,color); context2d.setGlobalAlpha(1.0f); if (getMap().getZoom() >= 11) { - context2d.setFillStyle(textColor); - double fontsize = 9.0 + (12.0-9.0)*(getMap().getZoom() - 10.0)/(12.0-10.0); - context2d.setFont("normal "+fontsize+"px Calibri"); - context2d.fillText(text, x + 0.7*fontsize, y + 0.3*fontsize); - //System.out.println("ZoomLevel: "+getMap().getZoomLevel()+", Fontsize: "+fontsize); + context2d.setFillStyle(textColor); + double fontsize = 9.0 + (12.0 - 9.0) * (getMap().getZoom() - 10.0) / (12.0 - 10.0); + context2d.setFont("normal " + fontsize + "px Calibri"); + context2d.fillText(text, x + 0.7 * fontsize, y + 0.3 * fontsize); + // System.out.println("ZoomLevel: "+getMap().getZoomLevel()+", Fontsize: "+fontsize); } } @@ -220,40 +209,27 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi String msg = "Wind @ P" + index + ": time : " + windDTO.timepoint + " speed: " + windDTO.trueWindSpeedInKnots + "knots " + windDTO.trueWindBearingDeg; logger.fine(msg); - PositionDTO position = windDTO.position; - LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); Point canvasPositionInPx = mapProjection.fromLatLngToDivPixel(positionLatLng); - double x = canvasPositionInPx.getX() - getWidgetPosLeft(); double y = canvasPositionInPx.getY() - getWidgetPosTop(); - //windFieldPoints.put(new ToolTip(x, y), windDTO); - drawArrowPx(x, y, angle, length, weight, drawHead, color); } protected void drawArrowPx(double x, double y, double angle, double length, double weight, boolean drawHead, String color) { final double dx = length * Math.sin(angle); final double dy = -length * Math.cos(angle); - final double x1 = x + dx / 2; final double y1 = y + dy / 2; - drawLine(x - dx / 2, y - dy / 2, x1, y1, weight, color); - final double theta = Math.atan2(-dy, dx); - final double hLength = Math.max(6.,6.+(10./(60.-10.))*Math.max(length-6.,0)); logger.finer("headlength: "+hLength+", arrowlength: "+length); - if (drawHead) { drawHead(x1, y1, theta, hLength, weight, color); } - //String text = "P" + index;// + NumberFormat.getFormat("0.00").format(windDTO.trueWindBearingDeg) + "�"; - //drawPointWithText(x, y, text); - //drawPoint(x, y); } protected void drawHead(final double x, final double y, final double theta, final double headLength, final double weight, String color) { @@ -265,7 +241,6 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi if (t2 <= (-Math.PI)) { t2 += 2 * Math.PI; } - final double x1 = (x - Math.cos(t) * headLength); final double y1 = (y + Math.sin(t) * headLength); final double x1o = (x + Math.cos(t) * weight/2); @@ -274,7 +249,6 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi final double y2 = (y + Math.sin(t2) * headLength); final double x2o = (x + Math.cos(t2) * weight/2); final double y2o = (y - Math.sin(t2) * weight/2); - drawLine(x1o, y1o, x1, y1, weight, color); drawLine(x2o, y2o, x2, y2, weight, color); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index cc9a8689b91..a8942c0d890 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -54,6 +54,7 @@ public class Swarm { public void start(int animationIntervalMillis, WindFieldDTO windField) { projection = new Mercator(fullcanvas, map); + // TODO make the VectorField a parameter of Swarm if (windField == null) { SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); String jsonStr = bundle.windStreamletsDataJS().getText(); From 450c4f5170f4aa558620ee26cf2d39f8352be300 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jun 2014 00:18:53 +0200 Subject: [PATCH 026/110] fixed NPE for particle's vector and previous pixel position --- .../com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java | 3 ++- .../com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index a8942c0d890..00c961e02ce 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -96,6 +96,7 @@ public class Swarm { particle.stepsToLive = 1 + (int) Math.round(Math.random() * 40); } particle.currentPixelCoordinate = projection.latlng2pixel(particle.currentPosition); + particle.previousPixelCoordinate = particle.currentPixelCoordinate; particle.v = v; done = true; } @@ -182,7 +183,7 @@ public class Swarm { if (particle.stepsToLive == 0) { continue; } - double particleSpeed = particle.v.length(); + double particleSpeed = particle.v == null ? 0 : particle.v.length(); ctxt.setLineWidth(field.lineWidth(particleSpeed)); ctxt.setStrokeStyle(field.getColor(particleSpeed)); ctxt.beginPath(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java index a24d9e40460..0ca8cf5fa89 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java @@ -25,4 +25,9 @@ public class Vector { } return this; } + + @Override + public String toString() { + return "{x: "+x+", y: "+y+"}"; + } } From 412dbae4c6cd7897da4c98d119398a3326a24f13 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 17 Jun 2014 09:31:39 +0200 Subject: [PATCH 027/110] fix updating issue when predict is pressed during replay; --- .../java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 924b9c422eb..7f0c0dbb757 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -641,6 +641,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ windParams.setyRes(yRes); windParams.setBorder(border); busyIndicator.setBusy(true); + timer.pause(); timer.setTime(windParams.getStartTime().getTime()); simulatorService.getWindField(windParams, windPatternDisplay, new AsyncCallback() { @@ -763,6 +764,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ windParams.setBorder(border); busyIndicator.setBusy(true); + timer.pause(); timer.setTime(windParams.getStartTime().getTime()); simulatorService.getSimulatorResults(mode, raceCourseDirection, windParams, windPatternDisplay, true, selection, new ResultManager(summaryView)); From 1dec1dc8ceead6dba35709c8fd526b87f78dfa64 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 17 Jun 2014 22:48:45 +0200 Subject: [PATCH 028/110] add URL-parameters to adjust streamlet visualization (dependent on screen, browser & device performance); --- .../gwt/ui/simulator/SimulatorEntryPoint.java | 36 ++++++++++++++++--- .../gwt/ui/simulator/SimulatorMainPanel.java | 6 ++-- .../gwt/ui/simulator/SimulatorMap.java | 14 ++++---- .../gwt/ui/simulator/StreamletParameters.java | 13 +++++++ .../WindStreamletsCanvasOverlay.java | 13 ++++--- .../ui/simulator/streamlets/RectField.java | 13 ++++--- .../simulator/streamlets/SimulatorField.java | 9 +++-- .../gwt/ui/simulator/streamlets/Swarm.java | 11 +++--- 8 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index 3a16138c492..f1f92d726d8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -26,7 +26,9 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { private int xRes = 40; private int yRes = 20; private int border = 0; - private int particles = 0; + + private StreamletParameters streamletPars = new StreamletParameters(); + private boolean autoUpdate = false; private char mode = SailingSimulatorConstants.ModeEvent; // default mode: 'e'vent private char event = SailingSimulatorConstants.EventKielerWoche; // default event: 'k'ieler woche @@ -39,6 +41,7 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { private boolean showStreamlets2 = false; // show animated wind streamlets in the wind display and replay modes. private boolean injectWindDataJS = false; + private static Logger logger = Logger.getLogger(SimulatorEntryPoint.class.getName()); @Override @@ -77,12 +80,37 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { } else { this.border = Integer.parseInt(border); } + + streamletPars.particles = 0; + streamletPars.motionScale = 1.0; + streamletPars.swarmScale = 1.0; + streamletPars.detailZoom = 15; + String particlesStr = Window.Location.getParameter("particles"); if (particlesStr == null || particlesStr.isEmpty()) { - logger.config("Using default particles " + this.particles); + logger.config("Using default particles " + this.streamletPars.particles); } else { - this.particles = Integer.parseInt(particlesStr); + this.streamletPars.particles = Integer.parseInt(particlesStr); } + String tmpStr = Window.Location.getParameter("motionScale"); + if (tmpStr == null || tmpStr.isEmpty()) { + logger.config("Using default motionScale."); + } else { + this.streamletPars.motionScale = Double.parseDouble(tmpStr); + } + tmpStr = Window.Location.getParameter("swarmScale"); + if (tmpStr == null || tmpStr.isEmpty()) { + logger.config("Using default swarmScale."); + } else { + this.streamletPars.swarmScale = Double.parseDouble(tmpStr); + } + tmpStr = Window.Location.getParameter("detailZoom"); + if (tmpStr == null || tmpStr.isEmpty()) { + logger.config("Using default detailZoom."); + } else { + this.streamletPars.detailZoom = Integer.parseInt(tmpStr); + } + String autoUpdateStr = Window.Location.getParameter("autoUpdate"); if (autoUpdateStr == null || autoUpdateStr.isEmpty()) { logger.config("Using default auto update " + autoUpdate); @@ -171,7 +199,7 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { } private void createSimulatorPanel() { - SimulatorMainPanel simulatorPanel = new SimulatorMainPanel(simulatorService, stringMessages, this, xRes, yRes, border, particles, + SimulatorMainPanel simulatorPanel = new SimulatorMainPanel(simulatorService, stringMessages, this, xRes, yRes, border, streamletPars, autoUpdate, mode, event, showGrid, showLines, seedLines, showArrows, showStreamlets, showStreamlets2, injectWindDataJS); DockLayoutPanel p = new DockLayoutPanel(Unit.PX); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java index 773ef8c98ff..1934128582a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java @@ -227,11 +227,11 @@ public class SimulatorMainPanel extends SimplePanel { return NumberFormat.getFormat("0.0").format(value); } - public SimulatorMainPanel(SimulatorServiceAsync svc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, int particles, boolean autoUpdate, + public SimulatorMainPanel(SimulatorServiceAsync svc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, StreamletParameters streamletPars, boolean autoUpdate, char mode, char event, boolean showGrid, boolean showLines, char seedLines, boolean showArrows, boolean showStreamlets, boolean showStreamlets2, boolean injectWindDataJS) { super(); - this.particles = particles; + this.particles = streamletPars.particles; this.simulatorSvc = svc; this.stringMessages = stringMessages; @@ -312,7 +312,7 @@ public class SimulatorMainPanel extends SimplePanel { busyIndicator = new SimpleBusyIndicator(false, 0.8f); - simulatorMap = new SimulatorMap(simulatorSvc, stringMessages, errorReporter, xRes, yRes, border, timer, timePanel, + simulatorMap = new SimulatorMap(simulatorSvc, stringMessages, errorReporter, xRes, yRes, border, streamletPars, timer, timePanel, windParams, busyIndicator, mode, this); simulatorMap.setSize("100%", "100%"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 7f0c0dbb757..87ca90d1ed8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -101,6 +101,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ private int xRes; private int yRes; private int border; + private StreamletParameters streamletPars; private boolean warningAlreadyShown = false; private SimulatorMainPanel parent = null; private PathPolyline pathPolyline = null; @@ -296,7 +297,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ } - public SimulatorMap(SimulatorServiceAsync simulatorSvc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, Timer timer, + /*public SimulatorMap(SimulatorServiceAsync simulatorSvc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, StreamletParameters streamletPars, Timer timer, WindFieldGenParamsDTO windParams, SimpleBusyIndicator busyIndicator, char mode, SimulatorMainPanel parent) { this.simulatorService = simulatorSvc; @@ -323,9 +324,9 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ this.timeListeners = new LinkedList(); this.initializeData(); this.parent = parent; - } + }*/ - public SimulatorMap(SimulatorServiceAsync simulatorSvc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, Timer timer, + public SimulatorMap(SimulatorServiceAsync simulatorSvc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, StreamletParameters streamletPars, Timer timer, TimePanel timePanel, WindFieldGenParamsDTO windParams, SimpleBusyIndicator busyIndicator, char mode, SimulatorMainPanel parent) { this.simulatorService = simulatorSvc; this.stringMessages = stringMessages; @@ -333,6 +334,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ this.xRes = xRes; this.yRes = yRes; this.border = border; + this.streamletPars = streamletPars; this.timer = timer; this.timePanel = timePanel; timer.addTimeListener(this); @@ -578,7 +580,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ windLineGuidesCanvasOverlay = new WindLineGuidesCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDSTREAMLETS_ZINDEX, timer, xRes); } if (windParams.isShowStreamlets2()) { - windStreamletsCanvasOverlay = new WindStreamletsCanvasOverlay(this, SimulatorMapOverlaysZIndexes.WINDFIELD_ZINDEX, timer, windParams); + windStreamletsCanvasOverlay = new WindStreamletsCanvasOverlay(this, SimulatorMapOverlaysZIndexes.WINDFIELD_ZINDEX, timer, streamletPars, windParams); } if (windParams.isShowGrid()) { windGridCanvasOverlay = new WindGridCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDGRID_ZINDEX, timer, xRes, yRes); @@ -1121,8 +1123,8 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ map.panTo(regattaAreaCanvasOverlay.getCurrentCourseArea().getCenterPos()); mapPan = true; - if (map.getZoom() < 14) { - map.setZoom(14); + if (map.getZoom() < streamletPars.detailZoom) { + map.setZoom(streamletPars.detailZoom); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java new file mode 100644 index 00000000000..897563b1834 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java @@ -0,0 +1,13 @@ +package com.sap.sailing.gwt.ui.simulator; + +public class StreamletParameters { + + public int particles = 0; + public double motionScale = 1.0; + public double swarmScale = 1.0; + public int detailZoom = 15; + + public StreamletParameters() { + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 0eb9261d174..b0967b5e7c9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -30,6 +30,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti protected WindFieldDTO windFieldDTO; protected WindFieldGenParamsDTO windParams = null; protected SimulatorMap simulatorMap; + protected StreamletParameters streamletPars; private boolean visible = false; private Timer timer; @@ -40,12 +41,13 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlay.class.getName()); - public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) { + public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, final StreamletParameters streamletPars, final WindFieldGenParamsDTO windParams) { super(simulatorMap.getMap(), zIndex); this.simulatorMap = simulatorMap; this.timer = timer; this.windParams = windParams; + this.streamletPars = streamletPars; this.nParticles = this.simulatorMap.getMainPanel().particles; @@ -136,9 +138,9 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti eval(jsonField); }-*/; - public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex) { + /*public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex) { this(simulatorMap, zIndex, null, null); - } + }*/ public native void setMapInstance(Object mapInstance) /*-{ $wnd.swarmMap = mapInstance; @@ -150,7 +152,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti public void startStreamlets() { if (swarm == null) { - this.swarm = new Swarm(this,map); + this.swarm = new Swarm(this, map, this.streamletPars); } this.swarm.start(40, windFieldDTO); } @@ -260,7 +262,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti super.draw(); if (mapProjection != null) { if ((nParticles > 0)&&(swarm == null)) { - this.swarm = new Swarm(this,map); + this.swarm = new Swarm(this, map, this.streamletPars); this.swarm.start(40, null); } if (windFieldDTO != null) { @@ -299,4 +301,5 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti public Timer getTimer() { return timer; } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index d80e55eb66e..c7f6b415695 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -5,6 +5,7 @@ import com.google.gwt.json.client.JSONParser; import com.google.gwt.maps.client.base.LatLng; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.gwt.ui.simulator.StreamletParameters; public class RectField implements VectorField { @@ -14,6 +15,8 @@ public class RectField implements VectorField { public double x1; public double y0; public double y1; + + private double motionFactor; //public double visX0; public double visX1; @@ -30,12 +33,14 @@ public class RectField implements VectorField { private double maxLength; public double particleFactor; - public RectField(Vector[][] field, double x0, double y0, double x1, double y1) { + public RectField(Vector[][] field, double x0, double y0, double x1, double y1, StreamletParameters parameters) { this.x0 = x0; this.x1 = x1; this.y0 = y0; this.y1 = y1; + this.motionFactor = 0.9 * parameters.motionScale; + this.visSW = new DegreePosition(0.0, 0.0); this.visNE = new DegreePosition(0.0, 0.0); @@ -60,7 +65,7 @@ public class RectField implements VectorField { my = (my / this.h) * (y1 - y0) + y0; } - public static RectField read(String jsonData, boolean correctForSphere) { + public static RectField read(String jsonData, boolean correctForSphere, StreamletParameters parameters) { JSONObject data = JSONParser.parseLenient(jsonData).isObject(); int w = (int)data.get("gridWidth").isNumber().doubleValue(); int h = (int)data.get("gridHeight").isNumber().doubleValue(); @@ -83,7 +88,7 @@ public class RectField implements VectorField { field[x][y] = v; } } - RectField result = new RectField(field, data.get("x0").isNumber().doubleValue(), data.get("y0").isNumber().doubleValue(), data.get("x1").isNumber().doubleValue(), data.get("y1").isNumber().doubleValue()); + RectField result = new RectField(field, data.get("x0").isNumber().doubleValue(), data.get("y0").isNumber().doubleValue(), data.get("x1").isNumber().doubleValue(), data.get("y1").isNumber().doubleValue(), parameters); return result; } @@ -144,7 +149,7 @@ public class RectField implements VectorField { } public double motionScale(int zoomLevel) { - return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); + return this.motionFactor * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); } public double particleWeight(Position p, Vector v) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 068a63f37d5..dc4d4eb8a22 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -9,6 +9,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; +import com.sap.sailing.gwt.ui.simulator.StreamletParameters; public class SimulatorField implements VectorField { @@ -36,6 +37,8 @@ public class SimulatorField implements VectorField { private double x1; private double y0; private double y1; + + private double motionFactor; public Position visSW; public Position visNE; @@ -54,7 +57,7 @@ public class SimulatorField implements VectorField { private double[][][] data; private int step; - public SimulatorField(WindFieldDTO windData, WindFieldGenParamsDTO windParams) { + public SimulatorField(WindFieldDTO windData, WindFieldGenParamsDTO windParams, StreamletParameters parameters) { this.step = 0; @@ -82,6 +85,8 @@ public class SimulatorField implements VectorField { this.y0 = baseData.get("boundsSW").isObject().get("lat").isNumber().doubleValue(); this.y1 = baseData.get("boundsNE").isObject().get("lat").isNumber().doubleValue(); + this.motionFactor = 0.07 * parameters.motionScale; + this.visSW = new DegreePosition(0.0, 0.0); this.visNE = new DegreePosition(0.0, 0.0); @@ -304,7 +309,7 @@ public class SimulatorField implements VectorField { } public double motionScale(int zoomLevel) { - return 0.07 * Math.pow(1.6, Math.min(1.0, 6.0 - zoomLevel)); + return this.motionFactor * Math.pow(1.6, Math.min(1.0, 6.0 - zoomLevel)); } public double particleWeight(Position p, Vector v) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 8678ac8d126..cc6df542ad7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -10,6 +10,7 @@ import com.google.gwt.user.client.Timer; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; +import com.sap.sailing.gwt.ui.simulator.StreamletParameters; import com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; @@ -18,6 +19,7 @@ public class Swarm { protected FullCanvasOverlay fullcanvas; protected Canvas canvas; protected MapWidget map; + protected StreamletParameters parameters; private Timer loopTimer; private String[] color; @@ -31,10 +33,11 @@ public class Swarm { private Position boundsNE; private Position boundsSW; - public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { + public Swarm(FullCanvasOverlay fullcanvas, MapWidget map, StreamletParameters streamletPars) { this.fullcanvas = fullcanvas; this.canvas = fullcanvas.getCanvas(); this.map = map; + this.parameters = streamletPars; } public void start(int animationDuration, WindFieldDTO windField) { @@ -45,7 +48,7 @@ public class Swarm { SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); String jsonStr = bundle.windStreamletsDataJS().getText(); - field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false); + field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false, this.parameters); map.setZoom(5); map.panTo(((RectField)field).getCenter()); @@ -54,7 +57,7 @@ public class Swarm { } else { - field = new SimulatorField(((WindStreamletsCanvasOverlay)fullcanvas).getWindFieldDTO(), ((WindStreamletsCanvasOverlay)fullcanvas).getWindParams()); + field = new SimulatorField(((WindStreamletsCanvasOverlay)fullcanvas).getWindFieldDTO(), ((WindStreamletsCanvasOverlay)fullcanvas).getWindParams(), this.parameters); fullcanvas.setCanvasSettings(); projection.calibrate(); @@ -182,7 +185,7 @@ public class Swarm { double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); - this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); + this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor() * this.parameters.swarmScale); //System.out.println("#particles: "+this.nParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); }; From 36db92c54239890745c0eabd4bad46a5d23d4067 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Wed, 18 Jun 2014 17:25:22 +0200 Subject: [PATCH 029/110] Tried to make export more robust against empty data; added some basic documentation --- .../sailing/xmlexport/LeaderboardData.java | 242 ++++++++++++------ 1 file changed, 159 insertions(+), 83 deletions(-) diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index 873d4e1435f..49de0cce90d 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -51,33 +51,7 @@ import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; /** - * Exports all data from a leaderboard into XML format. Format is as follows: - * - *

- * 
- *      XY
- *      
- *              1234
- *              
- *      
- *      
- *      
- *              R1
- *              
- *                      1234
- *                      1
- *                      10
- *              
- *              
- *                      1
- *                      
- *                              1234
- *                              123
- *                      
- *              
- *      
- * 
- * 
+ * Exports all data from a leaderboard into XML format. * * @author Simon Marcel Pamies */ @@ -98,6 +72,23 @@ public class LeaderboardData extends ExportAction { super(req, res, service); } + /** + * Creates XML for a leaderboard. Expects a list of generated competitors and races XML elements that are associated with the leaderboard. + *
+     *  
+     *     ESS 2014 Qingdao (Extreme40)
+     *     Qingdao
+     *     8000
+     *     HIGH_POINT_FIRST_GETS_TEN
+     *     Extreme40
+     *       
+     *       1.0
+     *       
+     *     
+     *     
+     *     
+     *  
+ */ private Element createLeaderboardXML(Leaderboard leaderboard, List competitors, List races, Util.Pair> leaderboardConfidenceAndErrorMessages) { Element leaderboardElement = new Element("leaderboard"); addNamedElementWithValue(leaderboardElement, "name", leaderboard.getName()); @@ -112,6 +103,20 @@ public class LeaderboardData extends ExportAction { return leaderboardElement; } + /** + * For the given {@link TimePoint} creates elements with several representations + * of that {@link TimePoint}. + *
+     *   2014
+     *   4
+     *   4
+     *   9
+     *   3
+     *   45
+     *   04.05.2014 09:03:45
+     *   1399194225000
+     * 
+ */ private List createTimedXML(String prefix, TimePoint timepoint) { List timedElements = new ArrayList(); Calendar timedDate = Calendar.getInstance(); @@ -141,6 +146,17 @@ public class LeaderboardData extends ExportAction { return timedElements; } + /** + * Creates elements related to the given speed fix. Implies that this is related to wind. + *
+     *       10.985139054212647
+     *       5.652159186143912
+     *       4.0
+     *       0.24242981000649663
+     *       Medium
+     *       8-12kn
+     * 
+ */ public List createWindXML(String prefix, SpeedWithConfidence speedWithConfidence) { List windElements = new ArrayList(); if (speedWithConfidence == null) { @@ -178,6 +194,9 @@ public class LeaderboardData extends ExportAction { return windElements; } + /** + * Creates elements containing information about a race + */ private Element createRaceXML(final TrackedRace race, final Fleet fleet, final List legs, final RaceColumn column, final Leaderboard leaderboard, int sameDayGroupIndex, int raceCounter, Util.Pair> raceConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { Element raceElement = new Element("race"); addNamedElementWithValue(raceElement, "name", cleanRaceName(race.getRace().getName())); @@ -211,7 +230,11 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(raceElement, "start_advantage_in_meters", start.getAdvantage().getMeters()); addNamedElementWithValue(raceElement, "advantageous_side_while_approaching_start_line", start.getAdvantageousSideWhileApproachingLine().name()); - addNamedElementWithValue(raceElement, "course_length_in_meters", race.getCourseLength().getMeters()); + Distance raceCourseLength = race.getCourseLength(); + if (raceCourseLength == null) { + raceConfidenceAndErrorMessages = updateConfidence("Race " + race.getRace().getName() + " has no course length.", 0.1, raceConfidenceAndErrorMessages); + } + addNamedElementWithValue(raceElement, "course_length_in_meters", raceCourseLength != null ? raceCourseLength.getMeters() : 0); raceElement.addContent(createWindXML("wind_", race.getAverageWindSpeedWithConfidence(/*resolutionInMillis*/ 5*60*1000))); final List allCompetitors = race.getCompetitorsFromBestToWorst(/*timePoint*/ race.getEndOfRace()); @@ -271,7 +294,7 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(competitorRaceDataElement, "final_race_score", finalRaceScore); competitorElement.addContent(competitorRaceDataElement); raceElement.addContent(competitorElement); - raceConfidenceAndErrorMessages.getB().add("Competitor " + competitorInLeaderboard.getName() + " has no valid data for this race!"); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitorInLeaderboard.getName() + " has no valid data for this race!", 0.2, raceConfidenceAndErrorMessages); additionalCompetitorCount++; } } @@ -292,7 +315,7 @@ public class LeaderboardData extends ExportAction { // we do not want to include competitors that did not start the race competitorElement.addContent(competitorRaceDataElement); raceElement.addContent(competitorElement); - raceConfidenceAndErrorMessages.getB().add("Competitor " + competitor.getName() + " has no valid data for this race!"); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid data for this race!", 0.1, raceConfidenceAndErrorMessages); continue; } @@ -312,58 +335,82 @@ public class LeaderboardData extends ExportAction { if (averageIntervallRaw != null) { addNamedElementWithValue(competitorRaceDataElement, "average_interval_between_fixes_raw_as_millis", averageIntervallRaw.asMillis()); } + } else { + addNamedElementWithValue(competitorRaceDataElement, "average_interval_between_fixes_outliers_removed_as_millis", 0); + addNamedElementWithValue(competitorRaceDataElement, "average_interval_between_fixes_raw_as_millis", 0); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid gps track for this race!", 0.5, raceConfidenceAndErrorMessages); } int[] timePointsInSecondsBeforeStart = new int[]{0, 5, 10, 20, 30}; for (int i : timePointsInSecondsBeforeStart) { TimePoint beforeRaceStartTime = race.getStartOfRace().minus(i*1000); - addNamedElementWithValue(competitorRaceDataElement, "distance_to_start_line_"+i+"seconds_before_start_in_meters", race.getDistanceToStartLine(competitor, beforeRaceStartTime).getMeters()); + Distance distanceToStartLineBeforeStart = race.getDistanceToStartLine(competitor, beforeRaceStartTime); + if (distanceToStartLineBeforeStart == null) { + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid distance to startline " + i + " seconds before start for this race!", 0.1, raceConfidenceAndErrorMessages); + } + addNamedElementWithValue(competitorRaceDataElement, "distance_to_start_line_"+i+"seconds_before_start_in_meters", distanceToStartLineBeforeStart != null ? distanceToStartLineBeforeStart.getMeters() : 0); Speed estimatedSpeedBeforeStarttime = race.getTrack(competitor).getEstimatedSpeed(beforeRaceStartTime); if (estimatedSpeedBeforeStarttime == null) { - raceConfidenceAndErrorMessages.getB().add("Competitor " + competitor.getName() + " has no valid speed " + i + " seconds before start for this race!"); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid speed " + i + " seconds before start for this race!", 0.1, raceConfidenceAndErrorMessages); } addNamedElementWithValue(competitorRaceDataElement, "speed_"+i+"seconds_before_start_of_race_in_knots", estimatedSpeedBeforeStarttime != null ? estimatedSpeedBeforeStarttime.getKnots() : 0); - addNamedElementWithValue(competitorRaceDataElement, "distance_from_starboard_side_of_start_line_"+i+"seconds_before_start_in_meters", race.getDistanceFromStarboardSideOfStartLine(competitor, beforeRaceStartTime).getMeters()); - Iterator marksForStartLine = race.getStartLine(beforeRaceStartTime).getWaypoint().getControlPoint().getMarks().iterator(); - Mark first = marksForStartLine.next(); - Mark second = null; - if (marksForStartLine.hasNext()) { - second = marksForStartLine.next(); - Position firstMarkPosition = race.getOrCreateTrack(first).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); - Position secondMarkPosition = race.getOrCreateTrack(second).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); - Position competitorPosition = race.getTrack(competitor).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); - if (firstMarkPosition != null && secondMarkPosition != null) { - Position projectedCompetitorPositionOntoStartLine = getOrthogonalProjectionOntoLine( - competitorPosition, - firstMarkPosition, - secondMarkPosition - ); - // now compute the distance from starboard mark - Mark starboardMark = race.getStartLine(beforeRaceStartTime).getStarboardMarkWhileApproachingLine(); - Mark portMark = null; - if (starboardMark.equals(first)) { - portMark = second; - } else { - portMark = first; + Distance distanceToStarboardSide = race.getDistanceFromStarboardSideOfStartLine(competitor, beforeRaceStartTime); + if (distanceToStarboardSide == null) { + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid distance to starboard side " + i + " seconds before start for this race!", 0.1, raceConfidenceAndErrorMessages); + } + addNamedElementWithValue(competitorRaceDataElement, "distance_from_starboard_side_of_start_line_"+i+"seconds_before_start_in_meters", distanceToStarboardSide != null ? distanceToStarboardSide.getMeters() : 0); + + LineDetails raceStartLine = race.getStartLine(beforeRaceStartTime); + if (raceStartLine == null) { + addNamedElementWithValue(competitorRaceDataElement, "bearing_fom_port_mark_to_starboard_mark"+i+"seconds_before_start_in_degrees", 0); + addNamedElementWithValue(competitorRaceDataElement, "bearing_fom_projected_position_to_starboard_mark"+i+"seconds_before_start_in_degrees", 0); + addNamedElementWithValue(competitorRaceDataElement, "bearing_to_starboard_mark_"+i+"seconds_before_start_in_degrees", 0); + addNamedElementWithValue(competitorRaceDataElement, "competitor_in_startline_box_"+i+"seconds_before_start", "false"); + addNamedElementWithValue(competitorRaceDataElement, "projected_starboard_mark_distance_"+i+"seconds_before_start_in_meters", 0); + addNamedElementWithValue(competitorRaceDataElement, "projected_position_onto_startline_"+i+"seconds_before_start", 0); + raceConfidenceAndErrorMessages = updateConfidence("Race " + race.getRace().getName() + " has no valid startline! Data related to startline will be wrong.", 0.2, raceConfidenceAndErrorMessages); + } else { + Iterator marksForStartLine = race.getStartLine(beforeRaceStartTime).getWaypoint().getControlPoint().getMarks().iterator(); + Mark first = marksForStartLine.next(); + Mark second = null; + if (marksForStartLine.hasNext()) { + second = marksForStartLine.next(); + Position firstMarkPosition = race.getOrCreateTrack(first).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); + Position secondMarkPosition = race.getOrCreateTrack(second).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); + Position competitorPosition = race.getTrack(competitor).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); + if (firstMarkPosition != null && secondMarkPosition != null) { + Position projectedCompetitorPositionOntoStartLine = getOrthogonalProjectionOntoLine( + competitorPosition, + firstMarkPosition, + secondMarkPosition + ); + // now compute the distance from starboard mark + Mark starboardMark = race.getStartLine(beforeRaceStartTime).getStarboardMarkWhileApproachingLine(); + Mark portMark = null; + if (starboardMark.equals(first)) { + portMark = second; + } else { + portMark = first; + } + Position portMarkPosition = race.getOrCreateTrack(portMark).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); + Position starboardMarkPosition = race.getOrCreateTrack(starboardMark).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); + Distance projectedDistance = projectedCompetitorPositionOntoStartLine.getDistance(starboardMarkPosition); + Bearing bearingToStarboardMarkFromCompetitorPosition = competitorPosition.getBearingGreatCircle(starboardMarkPosition); + Bearing bearingFromPortToStarboardMark = portMarkPosition.getBearingGreatCircle(starboardMarkPosition); + Bearing bearingFromProjectedCompetitorPositionToStarboardMark = projectedCompetitorPositionOntoStartLine.getBearingGreatCircle(starboardMarkPosition); + addNamedElementWithValue(competitorRaceDataElement, "bearing_fom_port_mark_to_starboard_mark"+i+"seconds_before_start_in_degrees", bearingFromPortToStarboardMark.getDegrees()); + addNamedElementWithValue(competitorRaceDataElement, "bearing_fom_projected_position_to_starboard_mark"+i+"seconds_before_start_in_degrees", bearingFromProjectedCompetitorPositionToStarboardMark.getDegrees()); + addNamedElementWithValue(competitorRaceDataElement, "bearing_to_starboard_mark_"+i+"seconds_before_start_in_degrees", bearingToStarboardMarkFromCompetitorPosition.getDegrees()); + if (Math.rint(bearingFromPortToStarboardMark.getDegrees()) == Math.rint(bearingFromProjectedCompetitorPositionToStarboardMark.getDegrees())) { + addNamedElementWithValue(competitorRaceDataElement, "competitor_in_startline_box_"+i+"seconds_before_start", "true"); + addNamedElementWithValue(competitorRaceDataElement, "projected_starboard_mark_distance_"+i+"seconds_before_start_in_meters", projectedDistance.getMeters()); + } else { + addNamedElementWithValue(competitorRaceDataElement, "competitor_in_startline_box_"+i+"seconds_before_start", "false"); + addNamedElementWithValue(competitorRaceDataElement, "projected_starboard_mark_distance_"+i+"seconds_before_start_in_meters", 0-projectedDistance.getMeters()); + } + addNamedElementWithValue(competitorRaceDataElement, "projected_position_onto_startline_"+i+"seconds_before_start", projectedCompetitorPositionOntoStartLine.toString()); } - Position portMarkPosition = race.getOrCreateTrack(portMark).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); - Position starboardMarkPosition = race.getOrCreateTrack(starboardMark).getEstimatedPosition(beforeRaceStartTime, /*extrapolate*/ false); - Distance projectedDistance = projectedCompetitorPositionOntoStartLine.getDistance(starboardMarkPosition); - Bearing bearingToStarboardMarkFromCompetitorPosition = competitorPosition.getBearingGreatCircle(starboardMarkPosition); - Bearing bearingFromPortToStarboardMark = portMarkPosition.getBearingGreatCircle(starboardMarkPosition); - Bearing bearingFromProjectedCompetitorPositionToStarboardMark = projectedCompetitorPositionOntoStartLine.getBearingGreatCircle(starboardMarkPosition); - addNamedElementWithValue(competitorRaceDataElement, "bearing_fom_port_mark_to_starboard_mark"+i+"seconds_before_start_in_degrees", bearingFromPortToStarboardMark.getDegrees()); - addNamedElementWithValue(competitorRaceDataElement, "bearing_fom_projected_position_to_starboard_mark"+i+"seconds_before_start_in_degrees", bearingFromProjectedCompetitorPositionToStarboardMark.getDegrees()); - addNamedElementWithValue(competitorRaceDataElement, "bearing_to_starboard_mark_"+i+"seconds_before_start_in_degrees", bearingToStarboardMarkFromCompetitorPosition.getDegrees()); - if (Math.rint(bearingFromPortToStarboardMark.getDegrees()) == Math.rint(bearingFromProjectedCompetitorPositionToStarboardMark.getDegrees())) { - addNamedElementWithValue(competitorRaceDataElement, "competitor_in_startline_box_"+i+"seconds_before_start", "true"); - addNamedElementWithValue(competitorRaceDataElement, "projected_starboard_mark_distance_"+i+"seconds_before_start_in_meters", projectedDistance.getMeters()); - } else { - addNamedElementWithValue(competitorRaceDataElement, "competitor_in_startline_box_"+i+"seconds_before_start", "false"); - addNamedElementWithValue(competitorRaceDataElement, "projected_starboard_mark_distance_"+i+"seconds_before_start_in_meters", 0-projectedDistance.getMeters()); - } - addNamedElementWithValue(competitorRaceDataElement, "projected_position_onto_startline_"+i+"seconds_before_start", projectedCompetitorPositionOntoStartLine.toString()); } } } @@ -373,12 +420,20 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(competitorRaceDataElement, "distance_to_start_line_on_race_start_in_meters", race.getDistanceToStartLine(competitor, race.getStartOfRace()).getMeters()); Speed estimatedSpeedAtStartSignal = race.getTrack(competitor).getEstimatedSpeed(race.getStartOfRace()); if (estimatedSpeedAtStartSignal == null) { - raceConfidenceAndErrorMessages.getB().add("Competitor " + competitor.getName() + " has no valid speed at start for this race!"); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid speed at start for this race!", 0.1, raceConfidenceAndErrorMessages); } addNamedElementWithValue(competitorRaceDataElement, "speed_on_start_signal_of_race_in_knots", estimatedSpeedAtStartSignal != null ? estimatedSpeedAtStartSignal.getKnots() : 0); - addNamedElementWithValue(competitorRaceDataElement, "distance_from_starboard_side_of_start_line_when_passing_start_in_meters", race.getDistanceFromStarboardSideOfStartLineWhenPassingStart(competitor).getMeters()); + Distance distanceFromStarboardSideWhenPassingStart = race.getDistanceFromStarboardSideOfStartLineWhenPassingStart(competitor); + if (distanceFromStarboardSideWhenPassingStart == null) { + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid distance from line at start for this race!", 0.1, raceConfidenceAndErrorMessages); + } + addNamedElementWithValue(competitorRaceDataElement, "distance_from_starboard_side_of_start_line_when_passing_start_in_meters", distanceFromStarboardSideWhenPassingStart != null ? distanceFromStarboardSideWhenPassingStart.getMeters() : 0); addNamedElementWithValue(competitorRaceDataElement, "rank_based_on_distance_from_starboard_side_of_start_line", competitorToDistanceRank.get(competitor)); - addNamedElementWithValue(competitorRaceDataElement, "speed_when_crossing_start_line_in_knots", race.getSpeedWhenCrossingStartLine(competitor).getKnots()); + Speed speedWhenCrossingStartline = race.getSpeedWhenCrossingStartLine(competitor); + if (speedWhenCrossingStartline == null) { + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid speed when passing startline for this race!", 0.2, raceConfidenceAndErrorMessages); + } + addNamedElementWithValue(competitorRaceDataElement, "speed_when_crossing_start_line_in_knots", speedWhenCrossingStartline != null ? speedWhenCrossingStartline.getKnots() : 0); com.sap.sse.common.Util.Triple gpsFixWithSpeedAndLegInformation = getMaximumSpeedOverGround(competitor, race); if (gpsFixWithSpeedAndLegInformation.getB() != null) { addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_in_knots", gpsFixWithSpeedAndLegInformation.getB().getKnots()); @@ -403,13 +458,13 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_reached_in_leg", 0); } } else { - raceConfidenceAndErrorMessages.getB().add("Competitor " + competitor.getName() + " has no valid maximum speed for this race!"); addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_in_knots", 0.0); competitorRaceDataElement.addContent(createTimedXML("maximum_race_speed_measured_at_", race.getStartOfRace())); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid maximum speed for this race!", 0.5, raceConfidenceAndErrorMessages); } Distance distanceTraveledInThisRace = race.getDistanceTraveled(competitor, race.getEndOfRace()); if (distanceTraveledInThisRace == null) { - raceConfidenceAndErrorMessages.getB().add("Competitor " + competitor.getName() + " has no valid distance traveled for this race!"); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid distance traveled for this race! Check distance traveled including non finished legs.", 0.3, raceConfidenceAndErrorMessages); } addNamedElementWithValue(competitorRaceDataElement, "distance_traveled_in_meters", distanceTraveledInThisRace == null ? 0.0 : distanceTraveledInThisRace.getMeters()); addNamedElementWithValue(competitorRaceDataElement, "distance_traveled_including_non_finished_legs_in_meters", getDistanceTraveled(race, competitor, race.getEndOfRace(), /*alsoReturnDistanceIfCompetitorHasNotFinishedRace*/ true).getMeters()); @@ -420,8 +475,8 @@ public class LeaderboardData extends ExportAction { if (trackedLegOfCompetitor != null && trackedLegOfCompetitor.getFinishTime() != null) { addNamedElementWithValue(competitorRaceDataElement, "rank_at_end_of_first_leg", trackedLegOfCompetitor.getRank(trackedLegOfCompetitor.getFinishTime())); } else { - raceConfidenceAndErrorMessages.getB().add("It seems that competitor " + competitor.getName() + " has not finished first leg!"); addNamedElementWithValue(competitorRaceDataElement, "rank_at_end_of_first_leg", 0); + raceConfidenceAndErrorMessages = updateConfidence("It seems that competitor " + competitor.getName() + " has not finished first leg!", 0.1, raceConfidenceAndErrorMessages); } Distance averageCrossTrackError = race.getAverageAbsoluteCrossTrackError(competitor, race.getStartOfRace(), race.getEndOfRace(), /*upwindOnly*/ false, /*waitForLatestAnalysis*/ false); addNamedElementWithValue(competitorRaceDataElement, "average_cross_track_error_in_meters", averageCrossTrackError != null ? averageCrossTrackError.getMeters() : -1.0); @@ -436,6 +491,10 @@ public class LeaderboardData extends ExportAction { return raceElement; } + /** + * Creates xml elements related to a competitor. + * @param shortVersion when set to true then only the name and UUID is returned + */ private Element createCompetitorXML(Competitor competitor, Leaderboard leaderboard, boolean shortVersion, Util.Pair> competitorConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { TimePoint timeSpent = MillisecondsTimePoint.now(); Element competitorElement = new Element("competitor"); @@ -478,7 +537,7 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(competitorElement, "total_time_sailed_including_non_finished_races_in_milliseconds", getTotalTimeSailedInMilliseconds(competitor, timePointOfLatestModification, true)); Distance totalDistanceSailed = leaderboard.getTotalDistanceTraveled(competitor, timePointOfLatestModification); if (totalDistanceSailed == null) { - competitorConfidenceAndErrorMessages.getB().add("Competitor has not finished all races in this leaderboard! His distance sailed is not comparable to others!"); + competitorConfidenceAndErrorMessages = updateConfidence("Competitor has not finished all races in this leaderboard! His distance sailed is not comparable to others!", 0.1, competitorConfidenceAndErrorMessages); } addNamedElementWithValue(competitorElement, "total_distance_sailed_in_meters", totalDistanceSailed != null ? totalDistanceSailed.getMeters() : 0); Distance totalDistanceSailedIncludingNonFinishedRaces = getTotalDistanceTraveled(leaderboard, competitor, timePointOfLatestModification); @@ -487,7 +546,7 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(competitorElement, "maximum_speed_over_ground_in_knots", maximumLeaderboardSpeedOverGround != null ? maximumLeaderboardSpeedOverGround.getB().getKnots() : 0); Speed averageSpeed = leaderboard.getAverageSpeedOverGround(competitor, timePointOfLatestModification); if (averageSpeed == null) { - competitorConfidenceAndErrorMessages.getB().add("Competitor has not finished all races in this leaderboard! His average speed over ground is not comparable to others!"); + competitorConfidenceAndErrorMessages = updateConfidence("Competitor has not finished all races in this leaderboard! His average speed over ground is not comparable to others!", 0.1, competitorConfidenceAndErrorMessages); } addNamedElementWithValue(competitorElement, "average_speed_over_ground_from_start_mark_passing_in_knots", averageSpeed != null ? averageSpeed.getKnots() : 0); Speed averageSpeedOverGroundIncludingNonCompletedRaces = getAverageSpeedOverGround(leaderboard, competitor, timePointOfLatestModification, true); @@ -500,6 +559,9 @@ public class LeaderboardData extends ExportAction { return competitorElement; } + /** + * Create xml elements related to a leg. + */ private Element createLegXML(TrackedLeg trackedLeg, Leaderboard leaderboard, int legCounter, Util.Pair> raceConfidenceAndErrorMessages, Util.Pair> legConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { TimePoint timeSpent = MillisecondsTimePoint.now(); Leg leg = trackedLeg.getLeg(); @@ -532,7 +594,7 @@ public class LeaderboardData extends ExportAction { Util.Pair maximumSpeed = competitorLeg.getMaximumSpeedOverGround(legFinishTime); addNamedElementWithValue(competitorLegDataElement, "maximum_speed_over_ground_in_knots", maximumSpeed != null ? maximumSpeed.getB().getKnots() : -1); if (maximumSpeed == null) { - legConfidenceAndErrorMessages.getB().add("Competitor "+ competitor.getName() +" has not finished this leg! His maximum speed for this leg is not comparable to others!"); + legConfidenceAndErrorMessages = updateConfidence("Competitor "+ competitor.getName() +" has not finished this leg! His maximum speed for this leg is not comparable to others!", 0.1, legConfidenceAndErrorMessages); } Speed averageVelocityMadeGood = competitorLeg.getAverageVelocityMadeGood(legFinishTime); addNamedElementWithValue(competitorLegDataElement, "average_velocity_made_good_in_knots", averageVelocityMadeGood != null ? averageVelocityMadeGood.getKnots() : 0); @@ -589,6 +651,9 @@ public class LeaderboardData extends ExportAction { return legElement; } + /** + * Creates xml elements that indicate confidence. + */ private Element createDataConfidenceXML(Util.Pair> confidenceAndMessages) { Element confidenceDataElement = new Element("confidence"); addNamedElementWithValue(confidenceDataElement, "simple_confidence_value", confidenceAndMessages.getA().doubleValue()); @@ -600,6 +665,17 @@ public class LeaderboardData extends ExportAction { return confidenceDataElement; } + /** + * Update confidence + * @param message The message that contains information why confidence is decreased + * @param decreaseConfidenceBy Value telling how much the confidence should be decreased + */ + private Util.Pair> updateConfidence(String message, double decreaseConfidenceBy, Util.Pair> confidencePair) { + Vector messages = confidencePair.getB(); + messages.add(message); + return new Util.Pair>(confidencePair.getA().doubleValue()-decreaseConfidenceBy, messages); + } + private Util.Pair> checkData(TrackedLeg leg) throws Exception { double simpleConfidence = 1.0; Vector messages = new Vector(); TimePoint now = MillisecondsTimePoint.now(); @@ -724,6 +800,9 @@ public class LeaderboardData extends ExportAction { return new Util.Pair>(simpleConfidence, messages); } + /** + * Groups races together that have been on the same day + */ private int getSameDayGroupIndex(TrackedRace currentRace, TrackedRace raceBefore) { if (raceBefore != null && !raceBefore.equals(currentRace)) { if (currentRace != null && currentRace.getStartOfRace() != null && raceBefore.getStartOfRace() != null) { @@ -782,10 +861,7 @@ public class LeaderboardData extends ExportAction { } } } - log.info("Finished XML export of leaderboard " + leaderboard.getName() + " in " + MillisecondsTimePoint.now().minus(timeSpent.asMillis()).asMillis() + " milliseconds"); sendDocument(createLeaderboardXML(leaderboard, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages), leaderboard.getName() + ".xml"); } - - } From ee480c086bc30a21b5c94fdac6fe1fd89a51bae2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 18 Jun 2014 18:25:46 +0200 Subject: [PATCH 030/110] fixing bug 2006 by stopping tracking in RacingEventService instead of only stopping the tracker when status FINISHED is reached --- .../impl/TracTracRaceTrackerImpl.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java index 9328a1fe7f9..cc5e8e43a74 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java @@ -210,6 +210,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements */ private boolean stopped; + private final TrackedRegattaRegistry trackedRegattaRegistry; + /** * Creates a race tracked for the specified URL/URIs and starts receiving all available existing and future push * data from there. Receiving continues until {@link #stop()} is called. @@ -294,6 +296,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword, String raceStatus, TrackedRegattaRegistry trackedRegattaRegistry) throws URISyntaxException, MalformedURLException, FileNotFoundException, SubscriberInitializationException { super(); + this.trackedRegattaRegistry = trackedRegattaRegistry; this.tractracRace = tractracRace; this.tractracEvent = tractracRace.getEvent(); urls = createID(paramURL, liveURI, storedURI); @@ -593,11 +596,13 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements updateStatusOfTrackedRaces(); if (!stopped) { try { - // See also bug 1517; with TracAPI we assume that when stopped(IEvent) is called by the TracAPI then - // all subscriptions have received all their data and it's therefore safe to stop all subscriptions - // at this point without missing any data. - stop(); - } catch (InterruptedException e) { + for (RaceDefinition race : getRaces()) { + // See also bug 1517; with TracAPI we assume that when stopped(IEvent) is called by the TracAPI then + // all subscriptions have received all their data and it's therefore safe to stop all subscriptions + // at this point without missing any data. + trackedRegattaRegistry.stopTracking(regatta, race); + } + } catch (InterruptedException | IOException e) { logger.log(Level.INFO, "Interrupted while trying to stop tracker "+this, e); } } From cc42f7273121bc360dbfb9f34b7f4d353c966cae Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Wed, 18 Jun 2014 20:30:42 +0200 Subject: [PATCH 031/110] Added russian translation for new column --- .../sap/sailing/gwt/ui/client/StringMessages_ru.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 01aa8f714ae..8524584cb21 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -982,7 +982,7 @@ pleaseSelectAScoringResult=Please select a scoring result... eventOfficialWebsiteURL=Official Website URL eventLogoImageURL=Logo Image URL showPingMarkMappings=Show Mappings for pinged Marks +showNumberOfRacesCompleted=Показать количество завершенных гонок +racesCompleted=Завершенные гонки +racesCompletedTooltip=Количество гонок завершенное участником в данной таблице лидеров regattaNameMustNotContainSlashes=Regatta name must not contain slashes -showNumberOfRacesCompleted=Show number of races completed -racesCompleted=Races completed -racesCompletedTooltip=Number of races the competitor has completed in this leaderboard. From 9d070ab9f3649c68f6afd279d892c86a09ec15ad Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 18 Jun 2014 21:34:06 +0200 Subject: [PATCH 032/110] add URL-parameters to adjust line-width: lineBase, lineScale; --- .../gwt/ui/simulator/SimulatorEntryPoint.java | 12 ++++++++++++ .../gwt/ui/simulator/StreamletParameters.java | 2 ++ .../gwt/ui/simulator/streamlets/SimulatorField.java | 6 +++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index b1a66451561..c568b392593 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -106,6 +106,18 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { } else { this.streamletPars.swarmScale = Double.parseDouble(tmpStr); } + tmpStr = Window.Location.getParameter("lineBase"); + if (tmpStr == null || tmpStr.isEmpty()) { + logger.config("Using default lineBase."); + } else { + this.streamletPars.lineBase = Double.parseDouble(tmpStr); + } + tmpStr = Window.Location.getParameter("lineScale"); + if (tmpStr == null || tmpStr.isEmpty()) { + logger.config("Using default lineScale."); + } else { + this.streamletPars.lineScale = Double.parseDouble(tmpStr); + } tmpStr = Window.Location.getParameter("detailZoom"); if (tmpStr == null || tmpStr.isEmpty()) { logger.config("Using default detailZoom."); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java index 897563b1834..40b49dd9578 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/StreamletParameters.java @@ -6,6 +6,8 @@ public class StreamletParameters { public double motionScale = 1.0; public double swarmScale = 1.0; public int detailZoom = 15; + public double lineScale = 1.0; + public double lineBase = 1.5; public StreamletParameters() { } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index c36a0c61398..506940bc4c5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -30,6 +30,8 @@ public class SimulatorField implements VectorField { private final double xScale; private double motionFactor; + private double lineBase; + private double lineScale; private final double maxLength; private final double particleFactor; @@ -64,6 +66,8 @@ public class SimulatorField implements VectorField { (this.rcStart.getLngDeg() - this.rcEnd.getLngDeg()) * bdPhi); this.xScale = windData.windData.xScale; this.motionFactor = 0.07 * parameters.motionScale; + this.lineBase = parameters.lineBase; + this.lineScale = parameters.lineScale; List gridData = windData.getMatrix(); int p = 0; int imax = windParams.getyRes() + 2 * windParams.getBorderY(); @@ -276,7 +280,7 @@ public class SimulatorField implements VectorField { * absolute linewidth speed == 12kn => linewidth 1.5 speed == 24kn => linewidth 3.0 speed == 6kn => linewidth * 0.75 */ - return Math.round(speed / 8.0 * 100.0) / 100.0; + return Math.max(0.01, Math.round((lineBase + lineScale * (speed - 12.0) / 8.0) * 100.0) / 100.0); } @Override From 806a409aada4d86e1ff9928b15212e725d34732f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 19 Jun 2014 00:13:52 +0200 Subject: [PATCH 033/110] towards a fix for bug 2005: compute regatta points sum for leaderboard charts using back-end scoring rules --- .../sap/sailing/domain/common/DetailType.java | 2 +- .../domain/leaderboard/Leaderboard.java | 39 ++++++++++++++++--- .../impl/AbstractSimpleLeaderboardImpl.java | 30 ++++++++++++-- .../gwt/ui/client/DetailTypeFormatter.java | 5 +++ .../gwt/ui/client/SailingServiceAsync.java | 2 +- .../AbstractCompetitorLeaderboardChart.java | 19 ++++----- ...aderboardChartSettingsDialogComponent.java | 2 +- .../ui/leaderboard/LeaderboardEntryPoint.java | 2 +- .../LeaderboardUrlConfigurationDialog.java | 6 +-- .../gwt/ui/server/SailingServiceImpl.java | 31 ++++++--------- 10 files changed, 93 insertions(+), 45 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java index f0ea050a3b4..99b5be250ff 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java @@ -36,7 +36,7 @@ public enum DetailType implements Serializable { MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING), TOTAL_DISTANCE_TRAVELED(0, ASCENDING), TOTAL_AVERAGE_SPEED_OVER_GROUND(2, DESCENDING), TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), - RACE_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS(2, ASCENDING), + RACE_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS_SUM(2, ASCENDING), RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(1, ASCENDING), RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING), RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING), RACE_TIME_TRAVELED(0, ASCENDING), RACE_TIME_TRAVELED_UPWIND(0, ASCENDING), RACE_TIME_TRAVELED_DOWNWIND(0, ASCENDING), RACE_TIME_TRAVELED_REACHING(0, ASCENDING), diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java index 42942f6ecd7..c248301d045 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java @@ -122,7 +122,7 @@ public interface Leaderboard extends LeaderboardBase { * For split fleets things can vary slightly. There, one fleet may complete a few races before the another fleet * starts with those races. In this case there isn't even any point in time at which all fleets have finished * exactly n races. Still, this method pretends such a time point would have existed, actually ignoring - * the times at which a race took place but only looking at the resulting scores and discard.

+ * the times at which a race took place but only looking at the resulting scores and discards.

* * When computing the ranks after all columns up to and including the race column that is the key of the resulting * map, the method applies the discarding and tie breaking rules as they would have had to be applied had the races @@ -133,6 +133,25 @@ public interface Leaderboard extends LeaderboardBase { */ Map> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint) throws NoWindException; + /** + * Computes the competitor's total points sum as they were or would have been after each race column (from left to right) + * was completed.

+ * + * A leaderboard fills up over time, usually "from left to right" with one race after another finishing. + * For split fleets things can vary slightly. There, one fleet may complete a few races before the another fleet + * starts with those races. In this case there isn't even any point in time at which all fleets have finished + * exactly n races. Still, this method pretends such a time point would have existed, actually ignoring + * the times at which a race took place but only looking at the resulting scores and discards.

+ * + * When computing the total points sum after all columns up to and including the race column that is the key of the resulting + * map, the method applies the discarding and tie breaking rules as they would have had to be applied had the races + * in the respective column just completed. + * + * @return The resulting map is guaranteed to have the same iteration order regarding the race columns + * as {@link #getRaceColumns()}. + */ + Map> getTotalPointsSumAfterRaceColumn(TimePoint timePoint) throws NoWindException; + /** * Tells the number of points carried over from previous races not tracked by this leaderboard for * the competitor. Returns 0 if there is no carried points definition for @@ -216,6 +235,14 @@ public interface Leaderboard extends LeaderboardBase { */ Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException; + /** + * Sums up the {@link #getTotalPoints(Competitor, RaceColumn, TimePoint) total points} of competitor + * across all race columns listed in raceColumnsToconsider, respecting the + * {@link RaceColumn#isStartsWithZeroScore()} property. + */ + Double getTotalPoints(Competitor competitor, Iterable raceColumnsToConsider, TimePoint timePoint) + throws NoWindException; + /** * Sorts the competitors according to their ranking in the race column specified. Only competitors who have a score * are added to the result list. This excludes competitors whose fleet hasn't raced for the raceColumn @@ -312,10 +339,11 @@ public interface Leaderboard extends LeaderboardBase { * Tells if the column represented by raceColumn shall be considered when counting the number of "races * so far" for discarding. Although medal races are never discarded themselves, they still count in determining the * number of "races so far" which is then the basis for deciding how many races may be discarded. If a leaderboard - * has corrections for a column for the competitor then that column shall be considered for discarding - * and counts for determining the number of races so far. Also, if a tracked race is connected to the column and has - * started already, the column is to be considered for discarding unless the column has several unordered fleets and - * not all fleets have started their race yet (see + * has corrections for a column for the competitor that are already to be applied at + * timePoint (e.g., because the race can be proven to have finished at timePoint), then + * that column shall be considered for discarding and counts for determining the number of races so far. Also, if a + * tracked race is connected to the column and has started already, the column is to be considered for discarding + * unless the column has several unordered fleets and not all fleets have started their race yet (see * {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, TimePoint)}). */ boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint); @@ -459,4 +487,5 @@ public interface Leaderboard extends LeaderboardBase { InterruptedException, ExecutionException; NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher(); + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index e61f6d14497..d03e3a0efa3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -520,15 +520,21 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race @Override public Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException { + return getTotalPoints(competitor, getRaceColumns(), timePoint); + } + + @Override + public Double getTotalPoints(Competitor competitor, final Iterable raceColumnsToConsider, + TimePoint timePoint) throws NoWindException { // when a column with isStartsWithZeroScore() is found, only reset score if the competitor scored in any race from there on boolean needToResetScoreUponNextNonEmptyEntry = false; double result = getCarriedPoints(competitor); - for (RaceColumn r : getRaceColumns()) { - if (r.isStartsWithZeroScore()) { + for (RaceColumn raceColumn : raceColumnsToConsider) { + if (raceColumn.isStartsWithZeroScore()) { needToResetScoreUponNextNonEmptyEntry = true; } - if (getScoringScheme().isValidInTotalScore(this, r, timePoint)) { - final Double totalPoints = getTotalPoints(competitor, r, timePoint); + if (getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint)) { + final Double totalPoints = getTotalPoints(competitor, raceColumn, raceColumnsToConsider, timePoint); if (totalPoints != null) { if (needToResetScoreUponNextNonEmptyEntry) { result = 0; @@ -690,6 +696,22 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race return result; } + @Override + public Map> getTotalPointsSumAfterRaceColumn(TimePoint timePoint) + throws NoWindException { + Map> result = new LinkedHashMap<>(); + List raceColumnsToConsider = new ArrayList<>(); + for (RaceColumn raceColumn : getRaceColumns()) { + raceColumnsToConsider.add(raceColumn); + Map totalPointsSumPerCompetitorInColumn = new HashMap<>(); + for (Competitor competitor : getCompetitors()) { + totalPointsSumPerCompetitorInColumn.put(competitor, getTotalPoints(competitor, raceColumnsToConsider, timePoint)); + } + result.put(raceColumn, totalPointsSumPerCompetitorInColumn); + } + return result; + } + @Override public Map, Entry> getContent(final TimePoint timePoint) throws NoWindException { Map, Entry> result = new HashMap, Entry>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java index 8f04b1f0212..74ef75acfd9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java @@ -28,6 +28,8 @@ public class DetailTypeFormatter { return stringMessages.totalPoints(); case REGATTA_TOTAL_POINTS: return stringMessages.totalPoints(); + case REGATTA_TOTAL_POINTS_SUM: + return stringMessages.totalPoints(); case NUMBER_OF_MANEUVERS: return stringMessages.numberOfManeuvers(); case CURRENT_SPEED_OVER_GROUND_IN_KNOTS: @@ -194,6 +196,7 @@ public class DetailTypeFormatter { case OVERALL_RANK: case RACE_TOTAL_POINTS: case REGATTA_TOTAL_POINTS: + case REGATTA_TOTAL_POINTS_SUM: case CURRENT_LEG: case TACK: case START_TACK: @@ -293,6 +296,8 @@ public class DetailTypeFormatter { return stringMessages.raceTotalPointsTooltip(); case REGATTA_TOTAL_POINTS: return stringMessages.regattaTotalPointsTooltip(); + case REGATTA_TOTAL_POINTS_SUM: + return stringMessages.regattaTotalPointsTooltip(); case RANK_GAIN: return stringMessages.rankGainTooltip(); case TACK: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index 2212ffc6447..7102feaa42a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -457,7 +457,7 @@ public interface SailingServiceAsync { /** * @param detailType - * supports {@link DetailType#REGATTA_RANK}, {@link DetailType#REGATTA_TOTAL_POINTS} and + * supports {@link DetailType#REGATTA_RANK}, {@link DetailType#REGATTA_TOTAL_POINTS_SUM} and * {@link DetailType#OVERALL_RANK}. * * @return the first triple element is the race column name; then follows the list of competitors, and finally the diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java index eaea356dada..f4b129302f8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java @@ -150,7 +150,7 @@ public abstract class AbstractCompetitorLeaderboardChart extends A chart.getYAxis().setReversed(true); chart.getYAxis().setTickInterval(1.0); break; - case REGATTA_TOTAL_POINTS: + case REGATTA_TOTAL_POINTS_SUM: chart.getYAxis().setTickInterval(5.0); chart.getYAxis().setReversed(false); break; @@ -231,12 +231,12 @@ public abstract class AbstractCompetitorLeaderboardChart extends A setWidget(chart); raceColumnNames.clear(); - switch(selectedDetailType) { + switch (selectedDetailType) { case OVERALL_RANK: case REGATTA_RANK: fillTotalRanksSeries(result, chartSeries); break; - case REGATTA_TOTAL_POINTS: + case REGATTA_TOTAL_POINTS_SUM: fillTotalPointsSeries(result, chartSeries); break; default: @@ -298,7 +298,9 @@ public abstract class AbstractCompetitorLeaderboardChart extends A chart.setHeight(maxCompetitorCount * 30 + 100 + "px"); } - private void fillTotalPointsSeries(List, List>> result, List chartSeries) { + private void fillTotalPointsSeries( + List, List>> result, + List chartSeries) { Double maxTotalPoints = 0.0; Set unusedSeries = new HashSet(competitorSeries.values()); for (Series series : competitorSeries.values()) { @@ -312,7 +314,7 @@ public abstract class AbstractCompetitorLeaderboardChart extends A for (com.sap.sse.common.Util.Triple, List> entry : result) { List dataValues = entry.getC(); raceColumnNames.add(entry.getA()); - if(hasValidValues(dataValues)) { + if (hasValidValues(dataValues)) { raceColumnNamesWithData.add(entry.getA()); int index = 0; maxCompetitorCount = Math.max(maxCompetitorCount, entry.getB().size()); @@ -320,18 +322,17 @@ public abstract class AbstractCompetitorLeaderboardChart extends A if (isCompetitorVisible(competitor)) { Series series = getOrCreateSeries(competitor); Double dataValue = dataValues.get(index); - if(dataValue != null) { + if (dataValue != null) { Double sumTotalPoints = dataValue; - if(sumTotalPointsMap.containsKey(competitor)) { + if (sumTotalPointsMap.containsKey(competitor)) { sumTotalPoints += sumTotalPointsMap.get(competitor); } sumTotalPointsMap.put(competitor, sumTotalPoints); series.addPoint(raceColumnNumber, sumTotalPoints, /* redraw */ false, /* shift */ false, /* animation */ false); - if(sumTotalPoints > maxTotalPoints) { + if (sumTotalPoints > maxTotalPoints) { maxTotalPoints = sumTotalPoints; } } - unusedSeries.remove(series); if (!chartSeries.contains(series)) { chart.addSeries(series); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorLeaderboardChartSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorLeaderboardChartSettingsDialogComponent.java index 8b81686a151..e050a2a9389 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorLeaderboardChartSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorLeaderboardChartSettingsDialogComponent.java @@ -27,7 +27,7 @@ public class MultiCompetitorLeaderboardChartSettingsDialogComponent implements S availableDetailsTypes = new ArrayList(); availableDetailsTypes.add(DetailType.REGATTA_RANK); - availableDetailsTypes.add(DetailType.REGATTA_TOTAL_POINTS); + availableDetailsTypes.add(DetailType.REGATTA_TOTAL_POINTS_SUM); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index dbdba4e74c3..90e34269819 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -171,7 +171,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint { String chartDetailParam = GwtHttpRequestUtils.getStringParameter(PARAM_CHART_DETAIL, null); DetailType chartDetailType; if (chartDetailParam != null && (DetailType.REGATTA_RANK.name().equals(chartDetailParam) || DetailType.OVERALL_RANK.name().equals(chartDetailParam) || - DetailType.REGATTA_TOTAL_POINTS.name().equals(chartDetailParam))) { + DetailType.REGATTA_TOTAL_POINTS_SUM.name().equals(chartDetailParam))) { chartDetailType = DetailType.valueOf(chartDetailParam); } else { chartDetailType = leaderboardType.isMetaLeaderboard() ? DetailType.OVERALL_RANK : DetailType.REGATTA_RANK; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardUrlConfigurationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardUrlConfigurationDialog.java index 859ea886079..e5aae8817c4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardUrlConfigurationDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardUrlConfigurationDialog.java @@ -172,12 +172,12 @@ public class LeaderboardUrlConfigurationDialog extends SettingsDialog, List> getTotalPointsForRaceColumn(LeaderboardDTO leaderboard, - RaceColumnDTO raceColumn) throws NoWindException { - List values = new ArrayList(); - List competitorDTOs = new ArrayList(); - for (Entry entry : leaderboard.rows.entrySet()) { - LeaderboardEntryDTO leaderboardEntryDTO = entry.getValue().fieldsByRaceColumnName.get(raceColumn.getName()); - values.add(leaderboardEntryDTO != null ? leaderboardEntryDTO.totalPoints : null); - competitorDTOs.add(entry.getKey()); - } - return new com.sap.sse.common.Util.Triple, List>(raceColumn.getName(), competitorDTOs, values); - } - @Override public List, List>> getLeaderboardDataEntriesForAllRaceColumns(String leaderboardName, Date date, DetailType detailType) throws Exception { @@ -2591,11 +2578,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } if (detailType != null) { switch (detailType) { - case REGATTA_TOTAL_POINTS: - LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, Collections. emptyList(), - /* addOverallDetails */ false, getService(), baseDomainFactory); - for (RaceColumnDTO raceColumnDTO : leaderboardDTO.getRaceList()) { - result.add(getTotalPointsForRaceColumn(leaderboardDTO, raceColumnDTO)); + case REGATTA_TOTAL_POINTS_SUM: + for (Entry> e : leaderboard.getTotalPointsSumAfterRaceColumn(timePoint).entrySet()) { + List competitorDTOs = new ArrayList<>(); + List pointSums = new ArrayList<>(); + for (Entry e2 : e.getValue().entrySet()) { + competitorDTOs.add(baseDomainFactory.convertToCompetitorDTO(e2.getKey())); + pointSums.add(e2.getValue()); + } + result.add(new Triple, List>(e.getKey().getName(), competitorDTOs, pointSums)); } break; case REGATTA_RANK: @@ -2613,7 +2604,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S competitorDTOs.add(baseDomainFactory.convertToCompetitorDTO(competitor)); rank++; } - result.add(new com.sap.sse.common.Util.Triple, List>(e.getKey().getName(), competitorDTOs, values)); + result.add(new Triple, List>(e.getKey().getName(), competitorDTOs, values)); } break; default: From 2c558ab6d4a589821405c96e723649c17323f009 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 19 Jun 2014 01:16:54 +0200 Subject: [PATCH 034/110] manual tests for bug 2005 fix looking good; added parallelization of total points sum calculation across race columns --- .../impl/RaceCourseReceiver.java | 6 +-- .../impl/AbstractSimpleLeaderboardImpl.java | 38 +++++++++++++++---- .../TracTracEventManagementPanel.java | 5 +++ .../AbstractCompetitorLeaderboardChart.java | 5 --- .../gwt/ui/server/SailingServiceImpl.java | 5 +-- 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java index 8951db82874..f1312486df9 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java @@ -182,7 +182,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue> getTotalPointsSumAfterRaceColumn(TimePoint timePoint) + public Map> getTotalPointsSumAfterRaceColumn(final TimePoint timePoint) throws NoWindException { - Map> result = new LinkedHashMap<>(); + final Map> result = new LinkedHashMap<>(); List raceColumnsToConsider = new ArrayList<>(); - for (RaceColumn raceColumn : getRaceColumns()) { + List> futures = new ArrayList<>(); + for (final RaceColumn raceColumn : getRaceColumns()) { raceColumnsToConsider.add(raceColumn); - Map totalPointsSumPerCompetitorInColumn = new HashMap<>(); - for (Competitor competitor : getCompetitors()) { - totalPointsSumPerCompetitorInColumn.put(competitor, getTotalPoints(competitor, raceColumnsToConsider, timePoint)); + final Iterable finalRaceColumnsToConsider = new ArrayList<>(raceColumnsToConsider); + futures.add(executor.submit(new Runnable() { + @Override + public void run() { + Map totalPointsSumPerCompetitorInColumn = new HashMap<>(); + for (Competitor competitor : getCompetitors()) { + try { + totalPointsSumPerCompetitorInColumn.put(competitor, getTotalPoints(competitor, finalRaceColumnsToConsider, timePoint)); + } catch (NoWindException e) { + throw new NoWindError(e); + } + } + synchronized (result) { + result.put(raceColumn, totalPointsSumPerCompetitorInColumn); + } + } + })); + } + for (Future future : futures) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + if (e.getCause() instanceof NoWindError) { + throw ((NoWindError) e.getCause()).getCause(); + } else { + throw new RuntimeException(e); // no caught exceptions occur in the futures executed + } } - result.put(raceColumn, totalPointsSumPerCompetitorInColumn); } return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java index f5f771ba1fa..9e8ad19460d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java @@ -296,6 +296,11 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel { List strings = new ArrayList(); strings.add(t.name); strings.add(t.regattaName); + if (t.boatClassNames != null) { + for (String boatClassName : t.boatClassNames) { + strings.add(boatClassName); + } + } return strings; } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java index f4b129302f8..cc0cafb22a9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorLeaderboardChart.java @@ -310,7 +310,6 @@ public abstract class AbstractCompetitorLeaderboardChart extends A } int raceColumnNumber = 0; int maxCompetitorCount = 0; - Map sumTotalPointsMap = new HashMap(); for (com.sap.sse.common.Util.Triple, List> entry : result) { List dataValues = entry.getC(); raceColumnNames.add(entry.getA()); @@ -324,10 +323,6 @@ public abstract class AbstractCompetitorLeaderboardChart extends A Double dataValue = dataValues.get(index); if (dataValue != null) { Double sumTotalPoints = dataValue; - if (sumTotalPointsMap.containsKey(competitor)) { - sumTotalPoints += sumTotalPointsMap.get(competitor); - } - sumTotalPointsMap.put(competitor, sumTotalPoints); series.addPoint(raceColumnNumber, sumTotalPoints, /* redraw */ false, /* shift */ false, /* animation */ false); if (sumTotalPoints > maxTotalPoints) { maxTotalPoints = sumTotalPoints; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 673221c8bd1..678dcdee5ec 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -2576,10 +2576,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } else { timePoint = new MillisecondsTimePoint(date); } + TimePoint effectiveTimePoint = timePoint == null ? leaderboard.getNowMinusDelay() : timePoint; if (detailType != null) { switch (detailType) { case REGATTA_TOTAL_POINTS_SUM: - for (Entry> e : leaderboard.getTotalPointsSumAfterRaceColumn(timePoint).entrySet()) { + for (Entry> e : leaderboard.getTotalPointsSumAfterRaceColumn(effectiveTimePoint).entrySet()) { List competitorDTOs = new ArrayList<>(); List pointSums = new ArrayList<>(); for (Entry e2 : e.getValue().entrySet()) { @@ -2591,8 +2592,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S break; case REGATTA_RANK: case OVERALL_RANK: - TimePoint effectiveTimePoint = timePoint == null ? - leaderboard.getNowMinusDelay() : timePoint; Map> competitorsFromBestToWorst = leaderboard .getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(effectiveTimePoint); for (Entry> e : competitorsFromBestToWorst.entrySet()) { From 48a9b24d58344ff514fe7c2d7091b24958f0234c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 19 Jun 2014 09:59:10 +0200 Subject: [PATCH 035/110] fixing bug 2005 ordering problem while still parallelizing --- .../impl/AbstractSimpleLeaderboardImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 1b395bebaa1..3de56864621 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -701,13 +701,13 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race throws NoWindException { final Map> result = new LinkedHashMap<>(); List raceColumnsToConsider = new ArrayList<>(); - List> futures = new ArrayList<>(); + Map>> futures = new HashMap<>(); for (final RaceColumn raceColumn : getRaceColumns()) { raceColumnsToConsider.add(raceColumn); final Iterable finalRaceColumnsToConsider = new ArrayList<>(raceColumnsToConsider); - futures.add(executor.submit(new Runnable() { + futures.put(raceColumn, executor.submit(new Callable>() { @Override - public void run() { + public Map call() { Map totalPointsSumPerCompetitorInColumn = new HashMap<>(); for (Competitor competitor : getCompetitors()) { try { @@ -717,14 +717,14 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race } } synchronized (result) { - result.put(raceColumn, totalPointsSumPerCompetitorInColumn); + return totalPointsSumPerCompetitorInColumn; } } })); } - for (Future future : futures) { + for (RaceColumn raceColumn : getRaceColumns()) { try { - future.get(); + result.put(raceColumn, futures.get(raceColumn).get()); } catch (InterruptedException | ExecutionException e) { if (e.getCause() instanceof NoWindError) { throw ((NoWindError) e.getCause()).getCause(); From b88de98f1fc541b2d5603bfe68f6c001cb6bf531 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 19 Jun 2014 15:31:30 +0200 Subject: [PATCH 036/110] added timeout rule for Igtimi tests --- .../sap/sailing/domain/igtimiadapter/impl/FixFactoryTest.java | 4 ++++ .../domain/igtimiadapter/impl/WebSocketServerListTest.java | 4 ++++ .../sailing/domain/igtimiadapter/impl/WindReceiverTest.java | 4 ++++ .../sap/sailing/domain/igtimiadapter/oauth/SignInTest.java | 4 ++++ .../igtimiadapter/test/AbstractTestWithIgtimiConnection.java | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactoryTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactoryTest.java index bcac9cd455e..86b74420709 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactoryTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/FixFactoryTest.java @@ -9,7 +9,9 @@ import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.igtimiadapter.datatypes.AntHrm; @@ -19,6 +21,8 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualitySatCount; import com.sap.sse.common.Util; public class FixFactoryTest { + @Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(2 * 60 * 1000); + private static final String demoJson = "{\n"+ "\"EA-AK-AAAG\": {\n"+ "\"1\": {\n"+ diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java index d4402812e86..014a61150ff 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java @@ -8,7 +8,9 @@ import java.net.URISyntaxException; import org.apache.http.client.ClientProtocolException; import org.json.simple.parser.ParseException; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory; import com.sap.sailing.mongodb.MongoDBConfiguration; @@ -16,6 +18,8 @@ import com.sap.sailing.mongodb.MongoDBService; import com.sap.sse.common.Util; public class WebSocketServerListTest { + @Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(2 * 60 * 1000); + @Test public void testWebSocketServerList() throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException { final ClientImpl testAppClient = new ClientImpl("7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6", diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WindReceiverTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WindReceiverTest.java index b6e47c1458c..da6c6e997ad 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WindReceiverTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WindReceiverTest.java @@ -10,7 +10,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import com.sap.sailing.declination.DeclinationService; import com.sap.sailing.domain.common.SpeedWithBearing; @@ -29,6 +31,8 @@ import com.sap.sailing.domain.igtimiadapter.shared.IgtimiWindReceiver; import com.sap.sailing.domain.tracking.Wind; public class WindReceiverTest { + @Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(2 * 60 * 1000); + @Test public void simpleWindReceiverTest() { final List windReceived = new ArrayList<>(); diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java index e3d4ede9840..a19667e5c1c 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java @@ -11,7 +11,9 @@ import javax.xml.parsers.ParserConfigurationException; import org.apache.http.client.ClientProtocolException; import org.json.simple.parser.ParseException; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import org.xml.sax.SAXException; import com.sap.sailing.domain.igtimiadapter.Account; @@ -27,6 +29,8 @@ import com.sap.sailing.mongodb.MongoDBService; public class SignInTest { private static final Logger logger = Logger.getLogger(SignInTest.class.getName()); + @Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(2 * 60 * 1000); + @Test public void testSimpleSignIn() throws ClientProtocolException, IOException, IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException, InstantiationException, diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/AbstractTestWithIgtimiConnection.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/AbstractTestWithIgtimiConnection.java index c66a62e8703..6f193bb4e03 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/AbstractTestWithIgtimiConnection.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/AbstractTestWithIgtimiConnection.java @@ -4,6 +4,8 @@ import java.io.IOException; import org.apache.http.client.ClientProtocolException; import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.Timeout; import com.sap.sailing.domain.igtimiadapter.Account; import com.sap.sailing.domain.igtimiadapter.IgtimiConnection; @@ -13,6 +15,8 @@ import com.sap.sailing.domain.igtimiadapter.impl.Activator; public class AbstractTestWithIgtimiConnection { protected IgtimiConnection connection; + @Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(2 * 60 * 1000); + @Before public void setUp() throws ClientProtocolException, IOException, org.json.simple.parser.ParseException { final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory(); From 63cf71047d12125bc6316db28363b5d1b1d604e5 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Fri, 20 Jun 2014 10:20:21 +0200 Subject: [PATCH 037/110] Changed column name to make purpose clearer --- .../com/sap/sailing/gwt/ui/client/StringMessages.java | 6 +++--- .../sap/sailing/gwt/ui/client/StringMessages.properties | 6 +++--- .../sailing/gwt/ui/client/StringMessages_de.properties | 6 +++--- .../sailing/gwt/ui/client/StringMessages_ru.properties | 8 ++++---- .../sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java | 2 +- .../leaderboard/LeaderboardSettingsDialogComponent.java | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 59c20eb382a..48e054d91be 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1044,7 +1044,7 @@ public interface StringMessages extends Messages { String eventLogoImageURL(); String showPingMarkMappings(); String regattaNameMustNotContainSlashes(); - String showNumberOfRacesCompleted(); - String racesCompleted(); - String racesCompletedTooltip(); + String showNumberOfRacesScored(); + String racesScored(); + String racesScoredTooltip(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 1f096d0397c..fa93e3cf9e9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1030,6 +1030,6 @@ eventOfficialWebsiteURL=Official Website URL eventLogoImageURL=Logo Image URL showPingMarkMappings=Show Mappings for pinged Marks regattaNameMustNotContainSlashes=Regatta name must not contain slashes -showNumberOfRacesCompleted=Show number of races completed -racesCompleted=Races completed -racesCompletedTooltip=Number of races the competitor has completed in this leaderboard. +showNumberOfRacesScored=Number of races scored +racesScored=Races scored +racesScoredTooltip=Number of races the competitor has completed or gotten a score for in this leaderboard. \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 7eee2bb1573..bb61930cd64 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1024,6 +1024,6 @@ eventOfficialWebsiteURL=URL der offiziellen Veranstaltungs-Website eventLogoImageURL=URL des Veranstaltungs-Logos showPingMarkMappings=Mappings für gepingte Marks zeigen regattaNameMustNotContainSlashes=Regattaname darf keine Schrägstriche enthalten -showNumberOfRacesCompleted=Anzahl vollendeter Rennen -racesCompleted=Vollendete Rennen -racesCompletedTooltip=Anzahl von Rennen, die der Segler vollendet hat +showNumberOfRacesScored=Anzahl bepunkteter Rennen +racesScored=Bepunktete Rennen +racesScoredTooltip=Anzahl von Rennen, die der Segler vollendet oder für die er Punkte bekommen hat \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 8524584cb21..7db8a9032a2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -982,7 +982,7 @@ pleaseSelectAScoringResult=Please select a scoring result... eventOfficialWebsiteURL=Official Website URL eventLogoImageURL=Logo Image URL showPingMarkMappings=Show Mappings for pinged Marks -showNumberOfRacesCompleted=Показать количество завершенных гонок -racesCompleted=Завершенные гонки -racesCompletedTooltip=Количество гонок завершенное участником в данной таблице лидеров -regattaNameMustNotContainSlashes=Regatta name must not contain slashes +showNumberOfRacesScored=Показать количество завершенных гонок +racesScored=Завершенные гонки +racesScoredTooltip=Количество гонок завершенное участником в данной таблице лидеров +regattaNameMustNotContainSlashes=Regatta name must not contain slashes \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index 62e37b2ca8b..f5280b01489 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -1336,7 +1336,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS @Override public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.racesCompleted()), stringMessages.racesCompletedTooltip()); + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.racesScored()), stringMessages.racesScoredTooltip()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java index 26e3ed31b0c..2c1e0f3df8c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java @@ -180,8 +180,8 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone overallDetailCheckboxes); overallDetailDialogContent.add(checkbox); } - showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox = dialog.createCheckbox(stringMessages.showNumberOfRacesCompleted()); - dialog.addTooltip(showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox, stringMessages.showNumberOfRacesCompleted()); + showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox = dialog.createCheckbox(stringMessages.showNumberOfRacesScored()); + dialog.addTooltip(showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox, stringMessages.showNumberOfRacesScored()); showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox.setValue(showOverallColumnWithNumberOfRacesSailedPerCompetitor); overallDetailDialogContent.add(showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox); overallDetailDialog.add(overallDetailDialogContent); From c55b3c2095ed58c2cd9a192edca6e5353fede3b0 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 20 Jun 2014 12:17:38 +0200 Subject: [PATCH 038/110] fixing construction of default regatta name for TracTrac connector, fixing bug 2007 --- .../domain/tractracadapter/impl/DomainFactoryImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index 3d42e8c15d5..b9d55ec30d9 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -372,7 +372,7 @@ public class DomainFactoryImpl implements DomainFactory { // This is particularly bad if a persistent regatta was loaded but a default regatta was accidentally created. // Then, there is no way but restart the server to get rid of this stale cache entry here. if (result == null) { - result = new RegattaImpl(raceLogStore, race.getName(), boatClass, trackedRegattaRegistry, + result = new RegattaImpl(raceLogStore, defaultRegattaNameAndBoatClass.getA(), boatClass, trackedRegattaRegistry, // use the low-point system as the default scoring scheme getBaseDomainFactory().createScoringScheme(ScoringSchemeType.LOW_POINT), race.getId(), null); regattaCache.put(key, result); @@ -389,7 +389,8 @@ public class DomainFactoryImpl implements DomainFactory { for (IRaceCompetitor competitor : race.getRaceCompetitors()) { competitorClassList.add(competitor.getCompetitor().getCompetitorClass()); } - Pair defaultRegattaNameAndBoatClass = new Pair(race.getName(), getDominantBoatClass(competitorClassList)); + Pair defaultRegattaNameAndBoatClass = new Pair(race.getEvent().getName(), + getDominantBoatClass(competitorClassList)); return defaultRegattaNameAndBoatClass; } From 6215e2750fce5313ab4265006310cf104472efdd Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Fri, 20 Jun 2014 12:17:49 +0200 Subject: [PATCH 039/110] Updated amazon ec2 backup strategy (markdown) --- wiki/amazon-ec2-backup-strategy.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/wiki/amazon-ec2-backup-strategy.md b/wiki/amazon-ec2-backup-strategy.md index 2ee451cc5b8..e9dbd4bd643 100644 --- a/wiki/amazon-ec2-backup-strategy.md +++ b/wiki/amazon-ec2-backup-strategy.md @@ -186,13 +186,15 @@ dir-etc mongodb-databases mysql-databases - Let's assume that you want to recover some score corrections from the archive server. We need to have a look at mongodb-databases and check what is contained there. Keep in mind that wind information is only updated once a week so that not every named backup will contain wind information.

-[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls mongodb-databases
-2014-05-27-170544 latest
-[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls mongodb-databases/latest
+[backup@ip-172-31-25-136 restored-files]$ /opt/bup/bup -d /home/backup/database/ ls databases/
+2014-05-27-212037  2014-05-29-233001  2014-06-01-233001  2014-06-04-203001  2014-06-06-233001  2014-06-09-233001  2014-06-11-233001  2014-06-14-233002  2014-06-17-233001  2014-06-19-233001                                        
+2014-05-28-203001  2014-05-30-233001  2014-06-02-233001  2014-06-04-233001  2014-06-07-233001  2014-06-10-233001  2014-06-12-233001  2014-06-15-233002  2014-06-18-203001  latest                                                   
+2014-05-28-233001  2014-05-31-233001  2014-06-03-233001  2014-06-05-233001  2014-06-08-233001  2014-06-11-203001  2014-06-13-233001  2014-06-16-233001  2014-06-18-233001
+[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls databases/latest
 var
-[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls mongodb-databases/latest/var/lib/mysql/backup
+[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls databases/latest/var/lib/mysql/backup
 mongodb-10201       mongodb-10201-wind  mongodb-10202       mongodb-10202-wind  mysql
-[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls mongodb-databases/latest/var/lib/mysql/backup/mongodb-10202
+[backup@ip-172-31-25-136 database]$ /opt/bup/bup -d /home/backup/database/ ls databases/latest/var/lib/mysql/backup/mongodb-10202
 COMMAND_MESSAGES.json         CONFIGURATIONS.json           LAST_MESSAGE_COUNT.json       LEADERBOARD_GROUPS.json       RACE_LOGS.json                REGATTA_FOR_RACE_ID.json      VIDEOS.json                                                 
 COMPETITORS.json              EVENTS.json                   LEADERBOARDS.json             RACES_MESSAGES.json           REGATTAS.json                 TRACTRAC_CONFIGURATIONS.json                                                              
 
From fd136e64a8e1c9b902c10d40b2a8ab874e610d86 Mon Sep 17 00:00:00 2001 From: Armin Zahamani Faharani Date: Fri, 20 Jun 2014 10:27:18 +0000 Subject: [PATCH 040/110] Updated home (markdown) --- home.md | 1 + 1 file changed, 1 insertion(+) diff --git a/home.md b/home.md index 8c04a5f5636..98cf01f3834 100644 --- a/home.md +++ b/home.md @@ -46,6 +46,7 @@ Like businesses, sailors need the latest information to make strategic decisions * [[Project Planning (bigger development)|wiki/planning]] * [[General Event Planning|wiki/general-event-planning]] * [[Information about Extreme Sailing Series|wiki/extreme-sailing-series]] + * [[Kieler Woche 2014 event page|wiki/kielerwoche2014]] ### Internal services (not related to wiki but useful) From e956367926d4fa994aa50c74c7165be2c90f77b4 Mon Sep 17 00:00:00 2001 From: Armin Zahamani Faharani Date: Fri, 20 Jun 2014 10:31:45 +0000 Subject: [PATCH 041/110] Created kielerwoche2014 (markdown) --- wiki/event-information-about-the-kieler-woche-2014.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 wiki/event-information-about-the-kieler-woche-2014.md diff --git a/wiki/event-information-about-the-kieler-woche-2014.md b/wiki/event-information-about-the-kieler-woche-2014.md new file mode 100644 index 00000000000..63e94f92d24 --- /dev/null +++ b/wiki/event-information-about-the-kieler-woche-2014.md @@ -0,0 +1,6 @@ +On this page, you will find all gathered information about the Kieler Woche 2014 for tracking, race management, etc.. + + +## Sailing Instructions + +## Race management plan \ No newline at end of file From 9a55ceffef05c05e051512d59859ffb36a090b70 Mon Sep 17 00:00:00 2001 From: Generic Wiki User Date: Fri, 20 Jun 2014 10:31:59 +0000 Subject: [PATCH 042/110] Destroyed event-information-about-the-kieler-woche-2014 (markdown) --- wiki/event-information-about-the-kieler-woche-2014.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 wiki/event-information-about-the-kieler-woche-2014.md diff --git a/wiki/event-information-about-the-kieler-woche-2014.md b/wiki/event-information-about-the-kieler-woche-2014.md deleted file mode 100644 index 63e94f92d24..00000000000 --- a/wiki/event-information-about-the-kieler-woche-2014.md +++ /dev/null @@ -1,6 +0,0 @@ -On this page, you will find all gathered information about the Kieler Woche 2014 for tracking, race management, etc.. - - -## Sailing Instructions - -## Race management plan \ No newline at end of file From a42db86b6f3b33e081234f7f22d9fd4f7a07fba8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 20 Jun 2014 20:06:38 +0200 Subject: [PATCH 043/110] After reverting the default regatta naming to the TracTrac event name, had to adjust a test verdict that expected different default regattas for different race names --- .../sap/sailing/domain/test/MultipleClassesInRegattaTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java index b40f4ac435b..58207d1a854 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java @@ -80,7 +80,6 @@ public class MultipleClassesInRegattaTest { assertEquals("505", kiwotest2.getRegatta().getBoatClass().getName()); assertEquals("49er", kiwotest3.getRegatta().getBoatClass().getName()); assertEquals("STG", weym470may112014_2.getRegatta().getBoatClass().getName()); - assertNotSame(weym470may112014_2.getRegatta(), kiwotest1.getRegatta()); // expecting that each race gets a distinct default regatta assertNotSame(kiwotest1.getRegatta(), kiwotest2.getRegatta()); assertNotSame(kiwotest1.getRegatta(), kiwotest3.getRegatta()); assertNotSame(kiwotest2.getRegatta(), kiwotest3.getRegatta()); From 095d4d1804161adb0d96b347e35d9cf77b0ac4df Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 08:35:54 +0200 Subject: [PATCH 044/110] use variables PWD and TMP in start script to allow overriding in env.sh --- java/target/start | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/java/target/start b/java/target/start index bbc17ceb861..5a435cc0fc6 100755 --- a/java/target/start +++ b/java/target/start @@ -1,7 +1,11 @@ #!/bin/sh # source variables -. `pwd`/env.sh +if [ "$TMP" = "" ]; then + TMP=/tmp +fi +PWD=`pwd` +. "$PWD/env.sh" PARAM=$@ @@ -36,13 +40,13 @@ if [[ $TELNET_ACTIVE != "" ]]; then fi scriptdir=`dirname "$0"` -LIB_DIR=`pwd`/native-libraries +LIB_DIR="$PWD/native-libraries" -if [[ ! -d `pwd`/logs ]]; then +if [[ ! -d "$PWD/logs" ]]; then mkdir logs fi -if [[ ! -d `pwd`/tmp ]]; then +if [[ ! -d "$PWD/tmp" ]]; then mkdir tmp fi @@ -72,7 +76,7 @@ APP_PARAMETERS="-Dmongo.host=$MONGODB_HOST -Dmongo.port=$MONGODB_PORT -Dmongo.db REPLICATION_PARAMETERS="-Dreplicate.on.start=$REPLICATE_ON_START -Dreplicate.master.servlet.host=$REPLICATE_MASTER_SERVLET_HOST -Dreplicate.master.servlet.port=$REPLICATE_MASTER_SERVLET_PORT -Dreplicate.master.queue.host=$REPLICATE_MASTER_QUEUE_HOST -Dreplicate.master.queue.port=$REPLICATE_MASTER_QUEUE_PORT" if [[ $PARAM == "" ]] || [[ $PARAM == "no-update" ]]; then - nohup $JAVA_BINARY -D$SERVER_NAME $ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.server.name=$SERVER_NAME $APP_PARAMETERS $REPLICATION_PARAMETERS -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Dosgi.shell.telnet.port=$TELNET_PORT -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar -configuration ./ -clean 2>/dev/null >/dev/null & + nohup $JAVA_BINARY -D$SERVER_NAME $ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.server.name=$SERVER_NAME $APP_PARAMETERS $REPLICATION_PARAMETERS -Djava.io.tmpdir="$PWD/tmp" -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Dosgi.shell.telnet.port=$TELNET_PORT -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar -configuration ./ -clean 2>/dev/null >/dev/null & JAVA_PID=$! echo "Starting server... (PID: $JAVA_PID)" echo "SERVER_NAME: $SERVER_NAME" @@ -127,10 +131,10 @@ if [[ $PARAM == "" ]] || [[ $PARAM == "no-update" ]]; then fi elif [[ $PARAM == "debug" ]]; then echo "Starting remote debugging session on port 8000 - make sure this port is open and reachable from the outside" - $JAVA_BINARY -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=y -D$SERVER_NAME $ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.server.name=$SERVER_NAME $APP_PARAMETERS $REPLICATION_PARAMETERS -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar -console -configuration ./ -clean + $JAVA_BINARY -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=y -D$SERVER_NAME $ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.server.name=$SERVER_NAME $APP_PARAMETERS $REPLICATION_PARAMETERS -Djava.io.tmpdir="$PWD/tmp" -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar -console -configuration ./ -clean elif [[ $PARAM == "fg" ]]; then - $JAVA_BINARY -D$SERVER_NAME $ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.server.name=$SERVER_NAME $APP_PARAMETERS $REPLICATION_PARAMETERS -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar -console -configuration ./ -clean + $JAVA_BINARY -D$SERVER_NAME $ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.server.name=$SERVER_NAME $APP_PARAMETERS $REPLICATION_PARAMETERS -Djava.io.tmpdir="$PWD/tmp" -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar -console -configuration ./ -clean fi From d70bc1e81744761b74b971f05bace750a7a0f044 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 09:05:00 +0200 Subject: [PATCH 045/110] fixed test after default regatta name is back to TracTrac event name + boat class --- .../server/replication/test/TrackRaceReplicationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index 9224cd3e056..8a7a9afad43 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -132,7 +132,7 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { // anyway. When the tracked race then is loaded it is expected // to automatically be linked to the leaderboard column. master.apply(new ConnectTrackedRaceToLeaderboardColumn(leaderboardName, columnName, defaultFleet.getName(), - new RegattaNameAndRaceName("weym470may122011 (STG)", "weym470may122011"))); + new RegattaNameAndRaceName("Academy Tracking 2011 (STG)", "weym470may122011"))); startTracking(); assertNotNull(masterColumn.getTrackedRace(defaultFleet)); // ensure the re-assignment worked on the master Thread.sleep(1000); From 492736098cba085464302ea64431ecf89fd5d4d3 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sat, 21 Jun 2014 09:49:40 +0200 Subject: [PATCH 046/110] Added persistence for result import urls --- .../persistence/DomainObjectFactory.java | 4 ++ .../persistence/MongoObjectFactory.java | 5 +++ .../persistence/impl/CollectionNames.java | 5 +++ .../impl/DomainObjectFactoryImpl.java | 23 ++++++++++++ .../domain/persistence/impl/FieldNames.java | 3 ++ .../impl/MongoObjectFactoryImpl.java | 17 +++++++++ .../freg/resultimport/impl/Activator.java | 4 +- .../gwt/ui/server/SailingServiceImpl.java | 16 ++++++-- .../resultimport/test/ParserTest.java | 13 +++++-- .../manage2sail/resultimport/Activator.java | 13 +++---- .../META-INF/MANIFEST.MF | 3 ++ .../sap/sailing/resultimport/Activator.java | 37 +++++++++++++++++++ .../resultimport/ResultUrlRegistry.java | 4 -- .../impl/ResultUrlRegistryImpl.java | 13 +++++-- 14 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java index ad5b3f1d655..da4f0b64fee 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java @@ -1,8 +1,10 @@ package com.sap.sailing.domain.persistence; +import java.net.URL; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import com.mongodb.DBObject; import com.sap.sailing.domain.base.Competitor; @@ -123,4 +125,6 @@ public interface DomainObjectFactory { DomainFactory getBaseDomainFactory(); Iterable> loadAllDeviceConfigurations(); + + Map> loadResultUrls(); } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java index b30a2603fc4..d6bf3eb7e23 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.persistence; import java.io.Serializable; +import java.net.URL; import com.mongodb.DB; import com.mongodb.DBObject; @@ -131,4 +132,8 @@ public interface MongoObjectFactory { void removeDeviceConfiguration(DeviceConfigurationMatcher matcher); void removeRaceLog(RaceLogIdentifier identifier); + void storeResultUrl(String resultProviderName, URL url); + + void removeResultUrl(String resultProviderName, URL url); + } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java index 4ab14d7457e..d97a965fb18 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java @@ -87,4 +87,9 @@ public enum CollectionNames { * Metadata for the GPSFixes, grouped by Device Identifier. */ GPS_FIXES_METADATA, + + /** + * URLs providing race results + */ + RESULT_URLS, } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index d9c2739e746..a9c49fc6e45 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -1760,4 +1760,27 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { stringRepresentation, deviceType, stringRepresentation); } } + + @Override + public Map> loadResultUrls() { + Map> resultUrls = new HashMap<>(); + DBCollection resultUrlCollection = database.getCollection(CollectionNames.RESULT_URLS.name()); + for (DBObject dbObject : resultUrlCollection.find()) { + String providerName = (String) dbObject.get(FieldNames.RESULT_PROVIDERNAME.name()); + String urlString = (String) dbObject.get(FieldNames.RESULT_URL.name()); + URL url; + try { + url = new URL(urlString); + } catch (MalformedURLException e) { + logger.log(Level.SEVERE, "Failed to parse result Url String: " + urlString + ". Did not load url!"); + continue; + } + if (!resultUrls.containsKey(providerName)) { + resultUrls.put(providerName, new HashSet()); + } + Set set = resultUrls.get(providerName); + set.add(url); + } + return resultUrls; + } } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java index 0022706c8d1..70b2e304856 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java @@ -108,4 +108,7 @@ public enum FieldNames { // Timespan FROM_MILLIS, TO_MILLIS, + + // Result Import url fields + RESULT_PROVIDERNAME, RESULT_URL } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index d3ae1ca81aa..72deaddc20c 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -1225,4 +1225,21 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { storeRaceLogIdentifier(identifier, query); getRaceLogCollection().remove(query); } + + @Override + public void storeResultUrl(String resultProviderName, URL url) { + DBCollection resultUrlsCollection = database.getCollection(CollectionNames.RESULT_URLS.name()); + DBObject query = new BasicDBObject(FieldNames.RESULT_PROVIDERNAME.name(), resultProviderName); + DBObject entry = new BasicDBObject(FieldNames.RESULT_PROVIDERNAME.name(), resultProviderName); + entry.put(FieldNames.RESULT_URL.name(), url.toString()); + resultUrlsCollection.update(query, entry, /* upsrt */true, /* multi */false, WriteConcern.SAFE); + } + + @Override + public void removeResultUrl(String resultProviderName, URL url) { + DBCollection resultUrlsCollection = database.getCollection(CollectionNames.RESULT_URLS.name()); + DBObject query = new BasicDBObjectBuilder().add(FieldNames.RESULT_PROVIDERNAME.name(), resultProviderName) + .add(FieldNames.RESULT_URL.name(), url.toString()).get(); + resultUrlsCollection.remove(query); + } } diff --git a/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java b/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java index 84fbf0686d2..6c0603a1e40 100755 --- a/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java +++ b/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java @@ -24,7 +24,9 @@ public class Activator implements BundleActivator { if (scanDirPath == null) { scanDirPath = System.getProperty(SCAN_DIR_PATH_PROPERTY_NAME, DEFAULT_SCAN_DIR); } - final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(ResultUrlRegistry.INSTANCE); + ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator + .getResultUrlRegistryService(bundleContext); + final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(resultUrlRegistry); bundleContext.registerService(ScoreCorrectionProvider.class, service, /* properties */null); logger.info("Scanning " + scanDirPath + " for official result lists of " + service.getName()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 678dcdee5ec..a940321d4db 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -50,6 +50,7 @@ import javax.servlet.ServletContext; import org.apache.http.client.ClientProtocolException; import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; @@ -3387,8 +3388,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public List getResultImportUrls(String resultProviderName) { List result = new ArrayList(); ResultUrlProvider urlBasedScoreCorrectionProvider = getUrlBasedScoreCorrectionProvider(resultProviderName); + ResultUrlRegistry resultUrlRegistry = getResultUrlRegistry(); if (urlBasedScoreCorrectionProvider != null) { - Iterable allUrls = ResultUrlRegistry.INSTANCE.getResultUrls(resultProviderName); + Iterable allUrls = resultUrlRegistry.getResultUrls(resultProviderName); for (URL url : allUrls) { result.add(url.toString()); } @@ -3399,9 +3401,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public void removeResultImportURLs(String resultProviderName, Set toRemove) throws Exception { ResultUrlProvider urlBasedScoreCorrectionProvider = getUrlBasedScoreCorrectionProvider(resultProviderName); + ResultUrlRegistry resultUrlRegistry = getResultUrlRegistry(); if (urlBasedScoreCorrectionProvider != null) { for (String urlToRemove : toRemove) { - ResultUrlRegistry.INSTANCE.unregisterResultUrl(resultProviderName, new URL(urlToRemove)); + resultUrlRegistry.unregisterResultUrl(resultProviderName, new URL(urlToRemove)); } } } @@ -3410,8 +3413,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public void addResultImportUrl(String resultProviderName, String url) throws Exception { ResultUrlProvider urlBasedScoreCorrectionProvider = getUrlBasedScoreCorrectionProvider(resultProviderName); if (urlBasedScoreCorrectionProvider != null) { - ResultUrlRegistry.INSTANCE.registerResultUrl(resultProviderName, new URL(url)); + ResultUrlRegistry resultUrlRegistry = getResultUrlRegistry(); + resultUrlRegistry.registerResultUrl(resultProviderName, new URL(url)); } + } + + private ResultUrlRegistry getResultUrlRegistry() { + ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator + .getResultUrlRegistryService(FrameworkUtil.getBundle(this.getClass()).getBundleContext()); + return resultUrlRegistry; } @Override diff --git a/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java b/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java index 2ea36a922fe..81ce87a81fb 100644 --- a/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java +++ b/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java @@ -19,6 +19,7 @@ import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import org.junit.Test; +import org.osgi.framework.FrameworkUtil; import org.xml.sax.SAXException; import com.sap.sailing.domain.common.RegattaScoreCorrections; @@ -95,8 +96,10 @@ public class ParserTest { @Test public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException, ParserConfigurationException, JAXBException { - ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(getTestDocumentProvider(), - ParserFactory.INSTANCE, ResultUrlRegistry.INSTANCE); + ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator + .getResultUrlRegistryService(FrameworkUtil.getBundle(this.getClass()).getBundleContext()); + ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl( + getTestDocumentProvider(), ParserFactory.INSTANCE, resultUrlRegistry); Map>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName(); Set> resultsForYES = hasResultsFor.get(YES_EVENT_NAME); @@ -107,8 +110,10 @@ public class ParserTest { @Test public void testScoreCorrectionProvider() throws Exception { - ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(getTestDocumentProvider(), - ParserFactory.INSTANCE, ResultUrlRegistry.INSTANCE); + ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator + .getResultUrlRegistryService(FrameworkUtil.getBundle(this.getClass()).getBundleContext()); + ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl( + getTestDocumentProvider(), ParserFactory.INSTANCE, resultUrlRegistry); Map>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName(); Set> resultsForYES = hasResultsFor.get(YES_EVENT_NAME); com.sap.sse.common.Util.Pair resultFor29er = null; diff --git a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java index 336fd74d11d..8db1029560a 100644 --- a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java +++ b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java @@ -1,6 +1,5 @@ package com.sap.sailing.manage2sail.resultimport; -import java.net.URL; import java.security.cert.CertificateException; import javax.net.ssl.HostnameVerifier; @@ -18,16 +17,14 @@ import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.xrr.resultimport.ParserFactory; public class Activator implements BundleActivator { - // private static String MANAGE2SAIL_KIELWEEK_2013_URL = "http://manage2sail.com/api/public/links/event/da33884e-24fe-44f6-8501-d253587f7cc8?accesstoken=bDAv8CwsTM94ujZ&mediaType=json"; - private static String MANAGE2SAIL_TW_2013_URL = "http://manage2sail.com/api/public/links/event/937a5c0a-5a28-4a07-806a-8c8ec1efc85c?accesstoken=bDAv8CwsTM94ujZ&mediaType=json"; public void start(BundleContext bundleContext) throws Exception { - final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(ParserFactory.INSTANCE, ResultUrlRegistry.INSTANCE); + ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator + .getResultUrlRegistryService(bundleContext); + final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(ParserFactory.INSTANCE, + resultUrlRegistry); bundleContext.registerService(ScoreCorrectionProvider.class, service, /* properties */null); - - // ResultUrlRegistry.INSTANCE.registerResultUrl(ScoreCorrectionProviderImpl.NAME, new URL(MANAGE2SAIL_KIELWEEK_2013_URL)); - ResultUrlRegistry.INSTANCE.registerResultUrl(ScoreCorrectionProviderImpl.NAME, new URL(MANAGE2SAIL_TW_2013_URL)); - + createAnAllCertificatesTrustingManagerforSSL(); } diff --git a/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF b/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF index bab08308e40..cbefbf7f2fe 100644 --- a/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF @@ -3,6 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Shared result import APIs Bundle-SymbolicName: com.sap.sailing.resultimport Bundle-Version: 1.0.0.qualifier +Bundle-Activator: com.sap.sailing.resultimport.Activator Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy @@ -11,5 +12,7 @@ Export-Package: com.sap.sailing.resultimport, Import-Package: com.sap.sailing.domain.base.impl, com.sap.sailing.domain.common, com.sap.sailing.domain.common.impl, + com.sap.sailing.domain.persistence, + com.sap.sailing.server, org.osgi.framework;version="1.5.0" Require-Bundle: com.sap.sse.common diff --git a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java new file mode 100644 index 00000000000..6ec38f12daf --- /dev/null +++ b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java @@ -0,0 +1,37 @@ +package com.sap.sailing.resultimport; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +import com.sap.sailing.resultimport.impl.ResultUrlRegistryImpl; +import com.sap.sailing.server.RacingEventService; + +public class Activator implements BundleActivator { + + @Override + public void start(BundleContext context) throws Exception { + BundleContext serverContext = FrameworkUtil.getBundle(RacingEventService.class).getBundleContext(); + ServiceReference racingEventServiceReference = serverContext + .getServiceReference(RacingEventService.class); + RacingEventService racingEventService = serverContext.getService(racingEventServiceReference); + + ResultUrlRegistry resultUrlRegistryService = new ResultUrlRegistryImpl( + racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory()); + context.registerService(ResultUrlRegistry.class, resultUrlRegistryService, null); + } + + @Override + public void stop(BundleContext context) throws Exception { + } + + public static ResultUrlRegistry getResultUrlRegistryService(BundleContext bundleContext) { + BundleContext resultImportBundleContext = FrameworkUtil.getBundle(ResultUrlRegistry.class).getBundleContext(); + ServiceReference serviceReference = resultImportBundleContext + .getServiceReference(ResultUrlRegistry.class); + ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceReference); + return resultUrlRegistry; + } + +} diff --git a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/ResultUrlRegistry.java b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/ResultUrlRegistry.java index fe03ffbc461..73243174f3e 100644 --- a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/ResultUrlRegistry.java +++ b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/ResultUrlRegistry.java @@ -2,15 +2,11 @@ package com.sap.sailing.resultimport; import java.net.URL; -import com.sap.sailing.resultimport.impl.ResultUrlRegistryImpl; - /** * A registry for result URL's of score correction providers * @author Frank */ public interface ResultUrlRegistry { - ResultUrlRegistry INSTANCE = new ResultUrlRegistryImpl(); - void registerResultUrl(String resultProviderName, URL url); void unregisterResultUrl(String resultProviderName, URL url); diff --git a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryImpl.java b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryImpl.java index 92c0fb8291f..b57ee771e9f 100644 --- a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryImpl.java +++ b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryImpl.java @@ -2,18 +2,21 @@ package com.sap.sailing.resultimport.impl; import java.net.URL; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.resultimport.ResultUrlRegistry; public class ResultUrlRegistryImpl implements ResultUrlRegistry { - private Map> resultUrls; + private final Map> resultUrls; + private final MongoObjectFactory mongoObjectFactory; - public ResultUrlRegistryImpl() { - resultUrls = new HashMap>(); + public ResultUrlRegistryImpl(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory) { + this.mongoObjectFactory = mongoObjectFactory; + resultUrls = domainObjectFactory.loadResultUrls(); } @Override @@ -24,6 +27,7 @@ public class ResultUrlRegistryImpl implements ResultUrlRegistry { resultUrls.put(resultProviderName, urlSet); } urlSet.add(url); + mongoObjectFactory.storeResultUrl(resultProviderName, url); } @Override @@ -31,6 +35,7 @@ public class ResultUrlRegistryImpl implements ResultUrlRegistry { Set urlSet = resultUrls.get(resultProviderName); if(urlSet != null) { urlSet.remove(url); + mongoObjectFactory.removeResultUrl(resultProviderName, url); } } From e244949a35dc4f05f8eb5449628210ff31bd54b7 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 10:33:19 +0200 Subject: [PATCH 047/110] fixed UI test after default regatta naming has reverted back to TracTrac event name + boat class name --- .../selenium/test/leaderboardedit/TestLeaderboardEditing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboardedit/TestLeaderboardEditing.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboardedit/TestLeaderboardEditing.java index 8120ed17cbc..cc0e6ec561b 100755 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboardedit/TestLeaderboardEditing.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboardedit/TestLeaderboardEditing.java @@ -32,7 +32,7 @@ public class TestLeaderboardEditing extends AbstractSeleniumTest { private static final TrackableRaceDescriptor TRACKABLE_RACE = new TrackableRaceDescriptor( BMW_CUP_REGATTA, BMW_CUP_RACE_8, BMW_CUP_BOAT_CLASS); private static final TrackedRaceDescriptor TRACKED_RACE = new TrackedRaceDescriptor( - BMW_CUP_RACE_8 + " (" + BMW_CUP_BOAT_CLASS + ")", BMW_CUP_BOAT_CLASS, BMW_CUP_RACE_8); + BMW_CUP_REGATTA + " (" + BMW_CUP_BOAT_CLASS + ")", BMW_CUP_BOAT_CLASS, BMW_CUP_RACE_8); private static final RaceDescriptor RACE_COLUMN = new RaceDescriptor("R1", "Default", false, false, 0.0); @Before From 631f6685a7489f0caaa938bdeb6e661aa38d520a Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sat, 21 Jun 2014 10:42:31 +0200 Subject: [PATCH 048/110] Added test for result url persistence --- .../META-INF/MANIFEST.MF | 11 ++++- .../resultimport/test/ParserTest.java | 13 +++-- .../resultimport/test/PersistenceTest.java | 49 +++++++++++++++++++ 3 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/PersistenceTest.java diff --git a/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF index dc666616874..5e21ee1aac3 100644 --- a/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF @@ -5,7 +5,14 @@ Bundle-SymbolicName: com.sap.sailing.manage2sail.resultimport.test Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.osgi.framework;version="1.5.0" +Import-Package: com.sap.sailing.domain.common, + com.sap.sailing.domain.leaderboard, + com.sap.sailing.domain.persistence, + com.sap.sailing.server, + com.sap.sailing.server.impl, + org.osgi.framework;version="1.5.0" Fragment-Host: com.sap.sailing.manage2sail.resultimport Require-Bundle: org.junit4;bundle-version="4.8.2", - com.sap.sse.common + com.sap.sse.common, + org.mockito.mockito-core;bundle-version="1.9.5", + org.objenesis;bundle-version="1.3.0" diff --git a/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java b/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java index 81ce87a81fb..921f2a988d5 100644 --- a/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java +++ b/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java @@ -2,6 +2,7 @@ package com.sap.sailing.manage2sail.resultimport.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; import java.io.File; import java.io.FileInputStream; @@ -19,18 +20,20 @@ import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import org.junit.Test; -import org.osgi.framework.FrameworkUtil; import org.xml.sax.SAXException; import com.sap.sailing.domain.common.RegattaScoreCorrections; import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.manage2sail.resultimport.ScoreCorrectionProviderImpl; import com.sap.sailing.resultimport.ResultDocumentDescriptor; import com.sap.sailing.resultimport.ResultDocumentProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.resultimport.impl.ResultDocumentDescriptorImpl; +import com.sap.sailing.resultimport.impl.ResultUrlRegistryImpl; import com.sap.sailing.xrr.resultimport.ParserFactory; import com.sap.sailing.xrr.resultimport.schema.RegattaResults; import com.sap.sse.common.Util; @@ -96,8 +99,8 @@ public class ParserTest { @Test public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException, ParserConfigurationException, JAXBException { - ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator - .getResultUrlRegistryService(FrameworkUtil.getBundle(this.getClass()).getBundleContext()); + ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mock(MongoObjectFactory.class), + mock(DomainObjectFactory.class)); ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl( getTestDocumentProvider(), ParserFactory.INSTANCE, resultUrlRegistry); Map>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName(); @@ -110,8 +113,8 @@ public class ParserTest { @Test public void testScoreCorrectionProvider() throws Exception { - ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator - .getResultUrlRegistryService(FrameworkUtil.getBundle(this.getClass()).getBundleContext()); + ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mock(MongoObjectFactory.class), + mock(DomainObjectFactory.class)); ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl( getTestDocumentProvider(), ParserFactory.INSTANCE, resultUrlRegistry); Map>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName(); diff --git a/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/PersistenceTest.java b/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/PersistenceTest.java new file mode 100644 index 00000000000..a6c7bcc8a59 --- /dev/null +++ b/java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/PersistenceTest.java @@ -0,0 +1,49 @@ +package com.sap.sailing.manage2sail.resultimport.test; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.Set; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.resultimport.ResultUrlRegistry; +import com.sap.sailing.resultimport.impl.ResultUrlRegistryImpl; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.impl.RacingEventServiceImpl; + +@SuppressWarnings("restriction") +public class PersistenceTest { + + @Test + public void testResultUrlStoring() throws MalformedURLException { + RacingEventService racingEventService = new RacingEventServiceImpl(); + DomainObjectFactory domainObjectFactory = racingEventService.getDomainObjectFactory(); + MongoObjectFactory mongoObjectFactory = racingEventService.getMongoObjectFactory(); + + ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mongoObjectFactory, domainObjectFactory); + + String testProviderName = "TestProvider"; + URL testUrl = new URL( + "http://manage2sail.com/api/public/links/event/d30883d3-2876-4d7e-af49-891af6cbae1b?accesstoken=bDAv8CwsTM94ujZ&mediaType=json"); + resultUrlRegistry.registerResultUrl(testProviderName, testUrl); + + Map> resultUrls = domainObjectFactory.loadResultUrls(); + Assert.assertTrue(resultUrls.containsKey(testProviderName)); + Set urls = resultUrls.get(testProviderName); + Assert.assertEquals(1, urls.size()); + URL url = urls.iterator().next(); + Assert.assertEquals(testUrl, url); + + resultUrlRegistry.unregisterResultUrl(testProviderName, testUrl); + + resultUrls = domainObjectFactory.loadResultUrls(); + Assert.assertFalse(resultUrls.containsKey(testProviderName)); + + } + +} From a0115e0e4cc4b0f3baf842a6f84e6b24678107bc Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 11:30:54 +0200 Subject: [PATCH 049/110] fixed synchronization issues with lastExpeditionMessages collection which was causing ConcurrentModificationExceptions --- .../server/gateway/impl/WindStatusServlet.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java index d34f511f52e..e5d1d4d78ad 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java @@ -101,8 +101,12 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim } } out.println("

Expedition Wind Status

"); - if (lastExpeditionMessages.size()>0) { - for (ExpeditionMessageInfo message : lastExpeditionMessages) { + if (!lastExpeditionMessages.isEmpty()) { + final List copyOfLastExpeditionMessages; + synchronized (lastExpeditionMessages) { + copyOfLastExpeditionMessages = new ArrayList<>(lastExpeditionMessages); + } + for (ExpeditionMessageInfo message : copyOfLastExpeditionMessages) { out.println(message); } } else { @@ -146,12 +150,14 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim receiver.addListener(new ExpeditionListener() { @Override public void received(ExpeditionMessage message) { - if(message != null && message.getBoatID() >= 0) { + if (message != null && message.getBoatID() >= 0) { ExpeditionMessageInfo info = new ExpeditionMessageInfo(); info.boatID = message.getBoatID(); info.message = message; info.messageReceivedAt = new Date(); - lastExpeditionMessages.add(info); + synchronized (lastExpeditionMessages) { + lastExpeditionMessages.add(info); + } } } } From 3b68ffbf9b07dd4fd9340ba0f38533366130574f Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Sat, 21 Jun 2014 13:53:59 +0200 Subject: [PATCH 050/110] Fixed leaderboards button --- .../regattaoverview/RegattaOverviewPanel.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java index edf3e2b65d6..125616eb04a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java @@ -186,27 +186,30 @@ public class RegattaOverviewPanel extends SimplePanel { flexTable.setWidget(0, 0, eventVenueGrid); flexTable.getCellFormatter().setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_MIDDLE); - boolean showLeaderboardButton = Window.Location.getParameter("enableLeaderboard") != null + final boolean showLeaderboardButton = Window.Location.getParameter("enableLeaderboard") != null && Window.Location.getParameter("enableLeaderboard").equalsIgnoreCase("true"); - if (showLeaderboardButton) { leaderboardCheckBox = addLeaderboardEnablerButton(); leaderboardCheckBox.getElement().getStyle().setMarginLeft(20.0, Unit.PX); - leaderboardsTabPanel = new TabPanel(); - leaderboardsTabPanel.setStyleName("RegattaOverview-Leaderboards"); - mainPanel.add(leaderboardsTabPanel); } else { leaderboardCheckBox = null; - leaderboardsTabPanel = null; } - + HorizontalPanel refreshStartStopClockPanel = getRefreshStartStopClockPanel(); - flexTable.setWidget(0, 1, refreshStartStopClockPanel); mainPanel.add(flexTable); mainPanel.add(regattaRaceStatesComponent); + if (showLeaderboardButton) { + leaderboardsTabPanel = new TabPanel(); + leaderboardsTabPanel.setStyleName("RegattaOverview-Leaderboards"); + leaderboardsTabPanel.setVisible(false); + mainPanel.add(leaderboardsTabPanel); + } else { + leaderboardsTabPanel = null; + } + onUpdateUI(uiUpdateTimer.getLiveTimePointAsDate()); } @@ -254,8 +257,13 @@ public class RegattaOverviewPanel extends SimplePanel { errorReporter, stringMessages, userAgent, /*showRaceDetails*/false); leaderboardsTabPanel.add(leaderboardPanel, leaderboard.getDisplayName() + " " + stringMessages.leaderboard()); } - leaderboardsTabPanel.setVisible(true); - leaderboardsTabPanel.selectTab(0); + if (!result.isEmpty()) { + leaderboardsTabPanel.setVisible(true); + leaderboardsTabPanel.selectTab(0); + } else { + leaderboardCheckBox.setValue(false); + errorReporter.reportError("Error trying to load leaderboard. Event could not be associated to Regatta."); + } } @Override public void onFailure(Throwable caught) { From bf84747cc22e1348923bd9c9911a4ab57d7a7267 Mon Sep 17 00:00:00 2001 From: fmittag Date: Sat, 21 Jun 2014 14:50:47 +0200 Subject: [PATCH 051/110] fixed the way the resultURLRegistry-Service is retreived from the OSGI service registry --- .../freg/resultimport/impl/Activator.java | 7 +++++-- .../gwt/ui/server/SailingServiceImpl.java | 16 ++++++++++++---- .../manage2sail/resultimport/Activator.java | 6 ++++-- .../com/sap/sailing/resultimport/Activator.java | 16 ++-------------- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java b/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java index 6c0603a1e40..ab2f589dfc0 100755 --- a/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java +++ b/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java @@ -4,6 +4,7 @@ import java.util.logging.Logger; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; import com.sap.sailing.domain.common.ScoreCorrectionProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; @@ -24,8 +25,10 @@ public class Activator implements BundleActivator { if (scanDirPath == null) { scanDirPath = System.getProperty(SCAN_DIR_PATH_PROPERTY_NAME, DEFAULT_SCAN_DIR); } - ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator - .getResultUrlRegistryService(bundleContext); + + ServiceReference serviceRefUrlRegistry = bundleContext.getServiceReference(ResultUrlRegistry.class); + ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceRefUrlRegistry); + final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(resultUrlRegistry); bundleContext.registerService(ScoreCorrectionProvider.class, service, /* properties */null); logger.info("Scanning " + scanDirPath + " for official result lists of " + service.getName()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index a940321d4db..a48b65e4c5e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -50,7 +50,6 @@ import javax.servlet.ServletContext; import org.apache.http.client.ClientProtocolException; import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; @@ -397,6 +396,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private final ServiceTracker replicationServiceTracker; + private final ServiceTracker resultUrlRegistryServiceTracker; + private final ServiceTracker scoreCorrectionProviderServiceTracker; private final MongoObjectFactory mongoObjectFactory; @@ -451,6 +452,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S context = Activator.getDefault(); racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context); replicationServiceTracker = createAndOpenReplicationServiceTracker(context); + resultUrlRegistryServiceTracker = createAndOpenResultUrlRegistryServiceTracker(context); swissTimingAdapterTracker = createAndOpenSwissTimingAdapterTracker(context); tractracAdapterTracker = createAndOpenTracTracAdapterTracker(context); raceLogTrackingAdapterTracker = createAndOpenRaceLogTrackingAdapterTracker(context); @@ -556,6 +558,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return result; } + protected ServiceTracker createAndOpenResultUrlRegistryServiceTracker( + BundleContext context) { + ServiceTracker result = new ServiceTracker( + context, ResultUrlRegistry.class.getName(), null); + result.open(); + return result; + } + /** * Asks the OSGi system for registered score correction provider services */ @@ -3419,9 +3429,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } private ResultUrlRegistry getResultUrlRegistry() { - ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator - .getResultUrlRegistryService(FrameworkUtil.getBundle(this.getClass()).getBundleContext()); - return resultUrlRegistry; + return resultUrlRegistryServiceTracker.getService(); } @Override diff --git a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java index 8db1029560a..077d987dac6 100644 --- a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java +++ b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java @@ -11,6 +11,7 @@ import javax.net.ssl.X509TrustManager; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; import com.sap.sailing.domain.common.ScoreCorrectionProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; @@ -19,8 +20,9 @@ import com.sap.sailing.xrr.resultimport.ParserFactory; public class Activator implements BundleActivator { public void start(BundleContext bundleContext) throws Exception { - ResultUrlRegistry resultUrlRegistry = com.sap.sailing.resultimport.Activator - .getResultUrlRegistryService(bundleContext); + ServiceReference serviceRefUrlRegistry = bundleContext.getServiceReference(ResultUrlRegistry.class); + ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceRefUrlRegistry); + final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(ParserFactory.INSTANCE, resultUrlRegistry); bundleContext.registerService(ScoreCorrectionProvider.class, service, /* properties */null); diff --git a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java index 6ec38f12daf..532586fc781 100644 --- a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java +++ b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java @@ -2,7 +2,6 @@ package com.sap.sailing.resultimport; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import com.sap.sailing.resultimport.impl.ResultUrlRegistryImpl; @@ -12,10 +11,8 @@ public class Activator implements BundleActivator { @Override public void start(BundleContext context) throws Exception { - BundleContext serverContext = FrameworkUtil.getBundle(RacingEventService.class).getBundleContext(); - ServiceReference racingEventServiceReference = serverContext - .getServiceReference(RacingEventService.class); - RacingEventService racingEventService = serverContext.getService(racingEventServiceReference); + ServiceReference racingEventServiceReference = context.getServiceReference(RacingEventService.class); + RacingEventService racingEventService = context.getService(racingEventServiceReference); ResultUrlRegistry resultUrlRegistryService = new ResultUrlRegistryImpl( racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory()); @@ -25,13 +22,4 @@ public class Activator implements BundleActivator { @Override public void stop(BundleContext context) throws Exception { } - - public static ResultUrlRegistry getResultUrlRegistryService(BundleContext bundleContext) { - BundleContext resultImportBundleContext = FrameworkUtil.getBundle(ResultUrlRegistry.class).getBundleContext(); - ServiceReference serviceReference = resultImportBundleContext - .getServiceReference(ResultUrlRegistry.class); - ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceReference); - return resultUrlRegistry; - } - } From 5a0af4f0d9e9102502e636df6b305a85ae930350 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sat, 21 Jun 2014 16:34:19 +0200 Subject: [PATCH 052/110] Added server trackers for the result url activation szenario --- .../META-INF/MANIFEST.MF | 3 +- .../freg/resultimport/impl/Activator.java | 26 ++++++++--- .../META-INF/MANIFEST.MF | 3 +- .../manage2sail/resultimport/Activator.java | 22 +++++++--- .../META-INF/MANIFEST.MF | 3 +- .../sap/sailing/resultimport/Activator.java | 43 ++++++++++++++++--- ...ltUrlRegistryServiceTrackerCustomizer.java | 40 +++++++++++++++++ 7 files changed, 119 insertions(+), 21 deletions(-) create mode 100644 java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryServiceTrackerCustomizer.java diff --git a/java/com.sap.sailing.freg.resultimport/META-INF/MANIFEST.MF b/java/com.sap.sailing.freg.resultimport/META-INF/MANIFEST.MF index e5a16da45d5..d4c04cfe69d 100755 --- a/java/com.sap.sailing.freg.resultimport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.freg.resultimport/META-INF/MANIFEST.MF @@ -8,7 +8,8 @@ Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: com.sap.sailing.resultimport, com.sap.sailing.resultimport.impl, - org.osgi.framework;version="1.3.0" + org.osgi.framework;version="1.3.0", + org.osgi.util.tracker;version="1.5.1" Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common, com.sap.sailing.domain, diff --git a/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java b/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java index ab2f589dfc0..9988a1a2361 100755 --- a/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java +++ b/java/com.sap.sailing.freg.resultimport/src/com/sap/sailing/freg/resultimport/impl/Activator.java @@ -4,10 +4,11 @@ import java.util.logging.Logger; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; import com.sap.sailing.domain.common.ScoreCorrectionProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; +import com.sap.sailing.resultimport.impl.ResultUrlRegistryServiceTrackerCustomizer; public class Activator implements BundleActivator { private static final Logger logger = Logger.getLogger(Activator.class.getName()); @@ -15,6 +16,8 @@ public class Activator implements BundleActivator { private static final String SCAN_DIR_PATH_PROPERTY_NAME = "freg.results"; private static final String DEFAULT_SCAN_DIR = "fregresults"; + private ServiceTracker resultUrlRegistryServiceTracker; + /* * For testing, consider using the following URLs: * allUrls.add(new URL("http://www.axel-uhl.de/freg/freg_html_export_sample.html")); @@ -25,15 +28,26 @@ public class Activator implements BundleActivator { if (scanDirPath == null) { scanDirPath = System.getProperty(SCAN_DIR_PATH_PROPERTY_NAME, DEFAULT_SCAN_DIR); } + logger.info("Scanning " + scanDirPath + " for official result lists of " + + ScoreCorrectionProviderImpl.PROVIDER_NAME); - ServiceReference serviceRefUrlRegistry = bundleContext.getServiceReference(ResultUrlRegistry.class); - ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceRefUrlRegistry); + resultUrlRegistryServiceTracker = new ServiceTracker<>(bundleContext, ResultUrlRegistry.class, + new ResultUrlRegistryServiceTrackerCustomizer(bundleContext) { + + @Override + protected ScoreCorrectionProvider configureScoreCorrectionProvider( + ResultUrlRegistry resultUrlRegistry) { + final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(resultUrlRegistry); + return service; + } + + }); + + resultUrlRegistryServiceTracker.open(); - final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(resultUrlRegistry); - bundleContext.registerService(ScoreCorrectionProvider.class, service, /* properties */null); - logger.info("Scanning " + scanDirPath + " for official result lists of " + service.getName()); } public void stop(BundleContext bundleContext) throws Exception { + resultUrlRegistryServiceTracker.close(); } } diff --git a/java/com.sap.sailing.manage2sail.resultimport/META-INF/MANIFEST.MF b/java/com.sap.sailing.manage2sail.resultimport/META-INF/MANIFEST.MF index f6797725212..9b1f4151689 100644 --- a/java/com.sap.sailing.manage2sail.resultimport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.manage2sail.resultimport/META-INF/MANIFEST.MF @@ -6,7 +6,8 @@ Bundle-Version: 1.0.0.qualifier Bundle-Activator: com.sap.sailing.manage2sail.resultimport.Activator Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: org.osgi.framework;version="1.5.0" +Import-Package: org.osgi.framework;version="1.5.0", + org.osgi.util.tracker;version="1.5.1" Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.manage2sail, com.sap.sailing.domain.common, diff --git a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java index 077d987dac6..1e810ca0f65 100644 --- a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java +++ b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Activator.java @@ -11,26 +11,36 @@ import javax.net.ssl.X509TrustManager; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; import com.sap.sailing.domain.common.ScoreCorrectionProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; +import com.sap.sailing.resultimport.impl.ResultUrlRegistryServiceTrackerCustomizer; import com.sap.sailing.xrr.resultimport.ParserFactory; public class Activator implements BundleActivator { + private ServiceTracker resultUrlRegistryServiceTracker; + public void start(BundleContext bundleContext) throws Exception { - ServiceReference serviceRefUrlRegistry = bundleContext.getServiceReference(ResultUrlRegistry.class); - ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceRefUrlRegistry); + resultUrlRegistryServiceTracker = new ServiceTracker<>(bundleContext, ResultUrlRegistry.class, + new ResultUrlRegistryServiceTrackerCustomizer(bundleContext) { - final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl(ParserFactory.INSTANCE, - resultUrlRegistry); - bundleContext.registerService(ScoreCorrectionProvider.class, service, /* properties */null); + @Override + protected ScoreCorrectionProvider configureScoreCorrectionProvider( + ResultUrlRegistry resultUrlRegistry) { + final ScoreCorrectionProviderImpl service = new ScoreCorrectionProviderImpl( + ParserFactory.INSTANCE, resultUrlRegistry); + return service; + } + }); + resultUrlRegistryServiceTracker.open(); createAnAllCertificatesTrustingManagerforSSL(); } public void stop(BundleContext bundleContext) throws Exception { + resultUrlRegistryServiceTracker.close(); } private void createAnAllCertificatesTrustingManagerforSSL() throws Exception { diff --git a/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF b/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF index cbefbf7f2fe..c05704b7e7b 100644 --- a/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.resultimport/META-INF/MANIFEST.MF @@ -14,5 +14,6 @@ Import-Package: com.sap.sailing.domain.base.impl, com.sap.sailing.domain.common.impl, com.sap.sailing.domain.persistence, com.sap.sailing.server, - org.osgi.framework;version="1.5.0" + org.osgi.framework;version="1.5.0", + org.osgi.util.tracker;version="1.5.1" Require-Bundle: com.sap.sse.common diff --git a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java index 532586fc781..32ccc218ff6 100644 --- a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java +++ b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/Activator.java @@ -3,23 +3,54 @@ package com.sap.sailing.resultimport; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; import com.sap.sailing.resultimport.impl.ResultUrlRegistryImpl; import com.sap.sailing.server.RacingEventService; public class Activator implements BundleActivator { + + private ServiceTracker racingEventServiceTracker; @Override public void start(BundleContext context) throws Exception { - ServiceReference racingEventServiceReference = context.getServiceReference(RacingEventService.class); - RacingEventService racingEventService = context.getService(racingEventServiceReference); - - ResultUrlRegistry resultUrlRegistryService = new ResultUrlRegistryImpl( - racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory()); - context.registerService(ResultUrlRegistry.class, resultUrlRegistryService, null); + racingEventServiceTracker = new ServiceTracker(context, + RacingEventService.class.getName(), new RacingEventServiceTrackerCustomizer(context)); + racingEventServiceTracker.open(); } @Override public void stop(BundleContext context) throws Exception { + racingEventServiceTracker.close(); + } + + private class RacingEventServiceTrackerCustomizer implements + ServiceTrackerCustomizer { + + private final BundleContext context; + + public RacingEventServiceTrackerCustomizer(BundleContext context) { + this.context = context; + } + + @Override + public RacingEventService addingService(ServiceReference reference) { + RacingEventService racingEventService = context.getService(reference); + + ResultUrlRegistry resultUrlRegistryService = new ResultUrlRegistryImpl( + racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory()); + context.registerService(ResultUrlRegistry.class, resultUrlRegistryService, null); + return racingEventService; + } + + @Override + public void modifiedService(ServiceReference reference, RacingEventService service) { + + } + + @Override + public void removedService(ServiceReference reference, RacingEventService service) { + } } } diff --git a/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryServiceTrackerCustomizer.java b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryServiceTrackerCustomizer.java new file mode 100644 index 00000000000..48db17c3005 --- /dev/null +++ b/java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/impl/ResultUrlRegistryServiceTrackerCustomizer.java @@ -0,0 +1,40 @@ +package com.sap.sailing.resultimport.impl; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTrackerCustomizer; + +import com.sap.sailing.domain.common.ScoreCorrectionProvider; +import com.sap.sailing.resultimport.ResultUrlRegistry; + +public abstract class ResultUrlRegistryServiceTrackerCustomizer implements + ServiceTrackerCustomizer { + + private final BundleContext bundleContext; + + public ResultUrlRegistryServiceTrackerCustomizer(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + @Override + public ResultUrlRegistry addingService(ServiceReference reference) { + ServiceReference serviceRefUrlRegistry = bundleContext + .getServiceReference(ResultUrlRegistry.class); + ResultUrlRegistry resultUrlRegistry = bundleContext.getService(serviceRefUrlRegistry); + + ScoreCorrectionProvider scoreCorrectionProvider = configureScoreCorrectionProvider(resultUrlRegistry); + bundleContext.registerService(ScoreCorrectionProvider.class, scoreCorrectionProvider, /* properties */null); + return resultUrlRegistry; + } + + protected abstract ScoreCorrectionProvider configureScoreCorrectionProvider(ResultUrlRegistry resultUrlRegistry); + + @Override + public void modifiedService(ServiceReference reference, ResultUrlRegistry service) { + } + + @Override + public void removedService(ServiceReference reference, ResultUrlRegistry service) { + } + +} From e121941f944547d155f7e8d1955648fc6d4cf6ee Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Sat, 21 Jun 2014 17:25:26 +0200 Subject: [PATCH 053/110] fix bug that prevents text view for flag change to be updated on RRS26 start mode change --- .../state/racingprocedure/impl/BaseRacingProcedure.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java index 52d74e54722..956d94bb26c 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java @@ -177,15 +177,18 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i protected void update() { boolean isRecallDisplayed = isRecallDisplayedAnalyzer.analyze(); + // always call listeners for changed flag, as this does not only affect recal, but also + // changes start procedure, for which some text elements need to be updated + // ({@link BaseRaceInfoFragmen#renderFlagChangesCountdown} + changedListeners.onActiveFlagsChanged(this); + if (cachedIsIndividualRecallDisplayed != isRecallDisplayed) { cachedIsIndividualRecallDisplayed = isRecallDisplayed; if (cachedIsIndividualRecallDisplayed) { changedListeners.onIndividualRecallDisplayed(this); - changedListeners.onActiveFlagsChanged(this); rescheduleIndividualRecallTimeout(recallDisplayedFinder.analyze()); } else { changedListeners.onIndividualRecallRemoved(this); - changedListeners.onActiveFlagsChanged(this); unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT); } } From f9d91834424ec1447c1382f6f950774bc98b648c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sat, 21 Jun 2014 17:50:15 +0200 Subject: [PATCH 054/110] viewShowNavigationPanel now enabled in RaceBoard and Spectator panels fixes bug2008 --- .../gwt/ui/raceboard/RaceBoardEntryPoint.java | 20 +++++++++++-------- .../gwt/ui/raceboard/RaceBoardPanel.java | 1 + .../raceboard/RaceBoardViewConfiguration.java | 12 +++++++++-- .../ui/spectator/LeaderboardGroupPanel.java | 7 ++++++- .../gwt/ui/spectator/SpectatorEntryPoint.java | 5 ++++- .../sap/sailing/gwt/ui/tv/TVEntryPoint.java | 5 +++-- 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index f1e650770ae..f3e26a58565 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -92,9 +92,10 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { final boolean canReplayWhileLiveIsPossible = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, false); final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, false); final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/); - raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard, showWindChart, - showCompetitorsChart, canReplayWhileLiveIsPossible, autoSelectMedia); - + final boolean showNavigationPanel = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_NAVIGATION_PANEL, true /* default */); + raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard, + showWindChart, showCompetitorsChart, canReplayWhileLiveIsPossible, autoSelectMedia, showNavigationPanel); + final ParallelExecutionCallback> getLeaderboardNamesCallback = new ParallelExecutionCallback>(); final ParallelExecutionCallback> getRegattasCallback = new ParallelExecutionCallback>(); final ParallelExecutionCallback getLeaderboardGroupByNameCallback = new ParallelExecutionCallback(); @@ -165,7 +166,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, user, timer, raceSelectionModel, leaderboardName, leaderboardGroupName, raceboardViewConfig, RaceBoardEntryPoint.this, stringMessages, userAgent, raceTimesInfoProvider, showMapControls); raceBoardPanel.fillRegattas(regattas); - createRaceBoardInOneScreenMode(raceBoardPanel); + createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig); } private RaceDTO findRace(String regattaName, String raceName, List regattas) { @@ -217,7 +218,8 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { return logoAndTitlePanel; } - private void createRaceBoardInOneScreenMode(RaceBoardPanel raceBoardPanel) { + private void createRaceBoardInOneScreenMode(RaceBoardPanel raceBoardPanel, + RaceBoardViewConfiguration raceboardViewConfiguration) { DockLayoutPanel p = new DockLayoutPanel(Unit.PX); RootLayoutPanel.get().add(p); Panel toolbarPanel = raceBoardPanel.getToolbarPanel(); @@ -229,9 +231,11 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); FlowPanel timePanel = createTimePanel(raceBoardPanel); p.addNorth(logoAndTitlePanel, 68); - p.addNorth(toolbarPanel, 40); - p.addSouth(timePanel, 90); + if (raceboardViewConfiguration.isShowNavigationPanel()) { + p.addNorth(toolbarPanel, 40); + } + p.addSouth(timePanel, 90); p.add(raceBoardPanel); p.addStyleName("dockLayoutPanel"); - } + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 3f7fe03fe10..2b8d3d7e920 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -163,6 +163,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra componentControlsPanel = new FlowPanel(); componentControlsPanel.addStyleName("raceBoardNavigation"); toolbarPanel.add(componentControlsPanel); + viewControlsPanel = new FlowPanel(); viewControlsPanel.addStyleName("raceBoardControls"); toolbarPanel.add(viewControlsPanel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardViewConfiguration.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardViewConfiguration.java index e5ff4373a5d..04be69bb8e0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardViewConfiguration.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardViewConfiguration.java @@ -12,6 +12,7 @@ public class RaceBoardViewConfiguration { private String activeCompetitorsFilterSetName; private boolean canReplayDuringLiveRaces; private final boolean autoSelectMedia; + private final boolean showNavigationPanel; public static final String PARAM_VIEW_MODE = "viewMode"; public static final String PARAM_VIEW_SHOW_LEADERBOARD = "viewShowLeaderboard"; @@ -28,16 +29,19 @@ public class RaceBoardViewConfiguration { showWindChart = false; showCompetitorsChart = false; autoSelectMedia = false; + showNavigationPanel = true; } public RaceBoardViewConfiguration(String activeCompetitorsFilterSetName, boolean showLeaderboard, - boolean showWindChart, boolean showCompetitorsChart, boolean canReplayDuringLiveRaces, boolean autoSelectMedia) { + boolean showWindChart, boolean showCompetitorsChart, boolean canReplayDuringLiveRaces, + boolean autoSelectMedia, boolean showNavigationPanel) { this.activeCompetitorsFilterSetName = activeCompetitorsFilterSetName; this.showLeaderboard = showLeaderboard; this.showWindChart = showWindChart; this.showCompetitorsChart = showCompetitorsChart; this.canReplayDuringLiveRaces = canReplayDuringLiveRaces; - this.autoSelectMedia = autoSelectMedia; + this.autoSelectMedia = autoSelectMedia; + this.showNavigationPanel = showNavigationPanel; } public boolean isShowLeaderboard() { @@ -64,4 +68,8 @@ public class RaceBoardViewConfiguration { return autoSelectMedia; } + public boolean isShowNavigationPanel() { + return showNavigationPanel; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupPanel.java index 59e30d9567c..443924b8975 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupPanel.java @@ -104,11 +104,13 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg private final boolean showRaceDetails; private final boolean canReplayDuringLiveRaces; private final boolean showMapControls; + private final boolean showNavigationPanel; private final Timer timerForClientServerOffset; public LeaderboardGroupPanel(SailingServiceAsync sailingService, StringMessages stringConstants, ErrorReporter errorReporter, final String groupName, String root, String viewMode, boolean embedded, - boolean showRaceDetails, boolean canReplayDuringLiveRaces, boolean showMapControls) { + boolean showRaceDetails, boolean canReplayDuringLiveRaces, boolean showMapControls, + boolean showNavigationPanel) { super(); this.isEmbedded = embedded; this.showRaceDetails = showRaceDetails; @@ -117,6 +119,7 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg this.sailingService = sailingService; this.stringMessages = stringConstants; this.errorReporter = errorReporter; + this.showNavigationPanel = showNavigationPanel; this.root = (root == null || root.length() == 0) ? "leaderboardGroupPanel" : root; this.viewMode = viewMode; setWidth("95%"); @@ -424,6 +427,8 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg linkParams.put(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, "true"); } linkParams.put(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, Boolean.toString(showMapControls)); + linkParams.put(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_NAVIGATION_PANEL, + Boolean.toString(showNavigationPanel)); linkParams.put("regattaName", raceIdentifier.getRegattaName()); linkParams.put("leaderboardGroupName", leaderboardGroup.getName()); if (viewMode != null && !viewMode.isEmpty()) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java index a773915e8a2..3a28e470c76 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java @@ -43,6 +43,8 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements RegattaRe RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, /* defaultValue */ false); final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter( RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, /* defaultValue */ true); + final boolean showNavigationPanel = GwtHttpRequestUtils.getBooleanParameter( + RaceBoardViewConfiguration.PARAM_VIEW_SHOW_NAVIGATION_PANEL, true /* default */); boolean showRaceDetails = Window.Location.getParameter("showRaceDetails") != null && Window.Location.getParameter("showRaceDetails").equalsIgnoreCase("true"); final String groupName; @@ -93,7 +95,8 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements RegattaRe rootPanel.add(groupOverviewPanel); } else { LeaderboardGroupPanel groupPanel = new LeaderboardGroupPanel(sailingService, stringMessages, this, - groupName, root, viewModeParamValue, embedded, showRaceDetails, canReplayDuringLiveRaces, showMapControls); + groupName, root, viewModeParamValue, embedded, showRaceDetails, canReplayDuringLiveRaces, + showMapControls, showNavigationPanel); groupAndFeedbackPanel.add(groupPanel); if (!embedded) { groupPanel.setWelcomeWidget(new SimpleWelcomeWidget(stringMessages.welcomeToSailingAnalytics(), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVEntryPoint.java index e0b90d5154f..1986cf1eadb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVEntryPoint.java @@ -58,8 +58,9 @@ public class TVEntryPoint extends AbstractEntryPoint { RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default */); String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/); final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, false); - raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, - showLeaderboard, showWindChart, showCompetitorsChart, /* canReplayWhileLiveIsPossible */ false, autoSelectMedia); + raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard, + showWindChart, showCompetitorsChart, /* canReplayWhileLiveIsPossible */false, autoSelectMedia, + showNavigationPanel); sailingService.getLeaderboardNames(new AsyncCallback>() { @Override From 3fcd8553c00afe5d00c40e0baa26c0d7dd3ea7a8 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Sat, 21 Jun 2014 18:06:09 +0200 Subject: [PATCH 055/110] Fixing display of expedition messages, also made sure to not display all messages at once --- .../sailing/server/gateway/impl/WindStatusServlet.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java index e5d1d4d78ad..d7c41050433 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java @@ -89,7 +89,7 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim IgtimiMessageInfo message = iterator.previous(); out.println(message); out.println("
"); - if (counter >= 10) { + if (counter >= 20) { break; } } @@ -106,8 +106,14 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim synchronized (lastExpeditionMessages) { copyOfLastExpeditionMessages = new ArrayList<>(lastExpeditionMessages); } + int expeditionMsgCounter = 0; for (ExpeditionMessageInfo message : copyOfLastExpeditionMessages) { + expeditionMsgCounter++; out.println(message); + out.println("
"); + if (expeditionMsgCounter >= 20) { + break; + } } } else { out.println("No Expedition messages received so far!"); From 5992db8fae937f47f9e4551f614696651780cd75 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Sat, 21 Jun 2014 18:13:09 +0200 Subject: [PATCH 056/110] Made igtimi messages more robust against concurrent modification exception --- .../gateway/impl/WindStatusServlet.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java index d7c41050433..1a51222ec23 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusServlet.java @@ -39,6 +39,8 @@ import com.sap.sailing.server.gateway.SailingServerHttpServlet; public class WindStatusServlet extends SailingServerHttpServlet implements IgtimiWindListener, BulkFixReceiver { private static final long serialVersionUID = -6791613843435003810L; + private final int NUMBER_OF_MESSAGES_TO_SHOW=20; + private static List lastExpeditionMessages; private static int igtimiRawMessageCount; @@ -82,14 +84,18 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim out.println(""); out.println(""); out.println("

Igtimi Wind Status ("+igtimiRawMessageCount+" raw messages received)

"); - if (lastIgtimiMessages != null && lastIgtimiMessages.size() > 0) { + if (lastIgtimiMessages != null && !lastIgtimiMessages.isEmpty()) { + final List copyOfLastIgtimiMessages; + synchronized (lastIgtimiMessages) { + copyOfLastIgtimiMessages = new ArrayList<>(WindStatusServlet.lastIgtimiMessages); + } int counter = 0; - for (ListIterator iterator = WindStatusServlet.lastIgtimiMessages.listIterator(WindStatusServlet.lastIgtimiMessages.size()); iterator.hasPrevious();) { + for (ListIterator iterator = copyOfLastIgtimiMessages.listIterator(copyOfLastIgtimiMessages.size()); iterator.hasPrevious();) { counter++; IgtimiMessageInfo message = iterator.previous(); out.println(message); out.println("
"); - if (counter >= 20) { + if (counter >= NUMBER_OF_MESSAGES_TO_SHOW) { break; } } @@ -101,17 +107,18 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim } } out.println("

Expedition Wind Status

"); - if (!lastExpeditionMessages.isEmpty()) { + if (lastExpeditionMessages != null && !lastExpeditionMessages.isEmpty()) { final List copyOfLastExpeditionMessages; synchronized (lastExpeditionMessages) { - copyOfLastExpeditionMessages = new ArrayList<>(lastExpeditionMessages); + copyOfLastExpeditionMessages = new ArrayList<>(WindStatusServlet.lastExpeditionMessages); } int expeditionMsgCounter = 0; - for (ExpeditionMessageInfo message : copyOfLastExpeditionMessages) { + for (ListIterator iterator = copyOfLastExpeditionMessages.listIterator(copyOfLastExpeditionMessages.size()); iterator.hasPrevious();) { expeditionMsgCounter++; + ExpeditionMessageInfo message = iterator.previous(); out.println(message); out.println("
"); - if (expeditionMsgCounter >= 20) { + if (expeditionMsgCounter >= NUMBER_OF_MESSAGES_TO_SHOW) { break; } } From a56e852f42892ec268a6b5b7ef324e667f691d7e Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Sat, 21 Jun 2014 18:24:01 +0200 Subject: [PATCH 057/110] change icons in Actionbar of RCApp to lighter version (better visibility) --- ...e_holo_light.png => ic_menu_share_holo_dark.png} | Bin .../res/values/themes.xml | 2 +- .../app/ui/activities/BaseActivity.java | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename mobile/com.sap.sailing.racecommittee.app/res/drawable/{ic_menu_share_holo_light.png => ic_menu_share_holo_dark.png} (100%) diff --git a/mobile/com.sap.sailing.racecommittee.app/res/drawable/ic_menu_share_holo_light.png b/mobile/com.sap.sailing.racecommittee.app/res/drawable/ic_menu_share_holo_dark.png similarity index 100% rename from mobile/com.sap.sailing.racecommittee.app/res/drawable/ic_menu_share_holo_light.png rename to mobile/com.sap.sailing.racecommittee.app/res/drawable/ic_menu_share_holo_dark.png diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/themes.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/themes.xml index 9ab853dba42..2eefe3b7581 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/themes.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/themes.xml @@ -9,7 +9,7 @@ @style/MyDropDownNav @style/Widget.Welter.ActionBar @color/welter_deep_gray - + @android:style/Widget.Holo.ActionButton.Overflow> diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/BaseActivity.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/BaseActivity.java index fa1b2f19734..f41c4c5b30e 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/BaseActivity.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/BaseActivity.java @@ -163,7 +163,7 @@ public abstract class BaseActivity extends LoggableActivity { sendingServiceStatus = String.format("Currently %d events waiting to be sent.\nLast successful sent was at %s", errorCount, lastSuccessfulSend == null ? "never" : lastSuccessfulSend); } else { - menuItemLive.setIcon(R.drawable.ic_menu_share_holo_light); + menuItemLive.setIcon(R.drawable.ic_menu_share); sendingServiceStatus = String.format("Currently no event waiting to be sent.", errorCount); } } From ed1d643a7650417591ddfe955e8e6095e3d5523c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sat, 21 Jun 2014 20:51:37 +0200 Subject: [PATCH 058/110] Added hamcrest to manifest because of mockito --- .../META-INF/MANIFEST.MF | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF index 5e21ee1aac3..60a830ec24c 100644 --- a/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF @@ -15,4 +15,5 @@ Fragment-Host: com.sap.sailing.manage2sail.resultimport Require-Bundle: org.junit4;bundle-version="4.8.2", com.sap.sse.common, org.mockito.mockito-core;bundle-version="1.9.5", - org.objenesis;bundle-version="1.3.0" + org.objenesis;bundle-version="1.3.0", + org.hamcrest;bundle-version="1.1.0" From 45b67de6a2b6e5b538dcc80f6963bf995a690495 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 21:56:15 +0200 Subject: [PATCH 059/110] working towards a solution for bug 2011; will cherry-pick to master and then continue there --- .../sailing/domain/base/impl/SharedDomainFactoryImpl.java | 3 +++ .../sap/sailing/domain/tracking/RaceChangeListener.java | 3 ++- .../src/com/sap/sailing/domain/tracking/TrackedRace.java | 7 +++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java index a1b9b451aa4..19e0bb03206 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java @@ -124,6 +124,9 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory { @Override public Nationality getOrCreateNationality(String threeLetterIOCCode) { + if (threeLetterIOCCode == null) { + threeLetterIOCCode = " "; + } synchronized (nationalityCache) { Nationality result = nationalityCache.get(threeLetterIOCCode); if (result == null) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceChangeListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceChangeListener.java index dca7dcf9a37..8c60bf9ffb4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceChangeListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceChangeListener.java @@ -3,13 +3,14 @@ package com.sap.sailing.domain.tracking; import java.util.Map; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CourseListener; import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WindSource; -public interface RaceChangeListener { +public interface RaceChangeListener extends CourseListener { void competitorPositionChanged(GPSFixMoving fix, Competitor competitor); void markPositionChanged(GPSFix fix, Mark mark); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 543620aef0b..a8c104c1c2e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -578,8 +578,11 @@ public interface TrackedRace extends Serializable { RaceLog getRaceLog(Serializable identifier); /** - * a setter for the listener on course design changes. The listener is mostly part of the tracking provider adapter. - * @param listener the listener to operate with. + * A setter for the listener on course design changes suggested by one of the {@link RaceLog}s attached to this + * race. The listener is mostly part of the tracking provider adapter. + * + * @param listener + * the listener to operate with. */ void addCourseDesignChangedListener(CourseDesignChangedListener listener); From c4221bc3d07bb180186137d878822a6db7a26ed4 Mon Sep 17 00:00:00 2001 From: fmittag Date: Sat, 21 Jun 2014 22:11:57 +0200 Subject: [PATCH 060/110] dirty fix for manage2sail result import where gender in XRR does not match gender in manage2sail json api --- .../manage2sail/resultimport/ScoreCorrectionProviderImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/ScoreCorrectionProviderImpl.java b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/ScoreCorrectionProviderImpl.java index 4faaf62e25c..4dc9ee1a665 100644 --- a/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/ScoreCorrectionProviderImpl.java +++ b/java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/ScoreCorrectionProviderImpl.java @@ -101,7 +101,7 @@ public class ScoreCorrectionProviderImpl implements ScoreCorrectionProvider, Res if(divisionGender != null) { divisionBoatClassAndGender += ", " + divisionGender.name(); } - if (boatClassName.equalsIgnoreCase(divisionBoatClassAndGender)) { + if (boatClassName.equalsIgnoreCase(divisionBoatClassAndGender) || boatClassName.contains(divisionBoatClassAndGender)) { return new XRRRegattaResultsAsScoreCorrections(event, division, this, parser); } From f49a34e7cf1539274e487f36438482fa4a6cbd38 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 22:14:19 +0200 Subject: [PATCH 061/110] invalidate cross track error cache entirely when course change is received --- .../impl/AbstractRaceChangeListener.java | 8 +++++++ .../tracking/impl/CrossTrackErrorCache.java | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceChangeListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceChangeListener.java index c60e9caf80d..b931fb325f3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceChangeListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceChangeListener.java @@ -59,5 +59,13 @@ public abstract class AbstractRaceChangeListener implements RaceChangeListener { @Override public void delayToLiveChanged(long delayToLiveInMillis) { } + + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java index c39dcb8f942..26232c9cad4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java @@ -434,6 +434,27 @@ public class CrossTrackErrorCache extends AbstractRaceChangeListener { } } + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + invalidate(); + } + + /** + * Invalidates all cache contents for all competitors from the beginning of time + */ + private void invalidate() { + final List shuffledCompetitors = new ArrayList<>(cachePerCompetitor.keySet()); + Collections.shuffle(shuffledCompetitors); + for (Competitor competitor : shuffledCompetitors) { + invalidate(competitor, /* from the beginning of time */ null); + } + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + invalidate(); + } + @Override public String toString() { return "CrossTrackErrorCache for competitors "+cachePerCompetitor.keySet(); From 9adc2c2999bb389a4eef95365ed70a153157c34a Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 22:22:04 +0200 Subject: [PATCH 062/110] added event forwarding for CourseListener events to DynamicTrackedRace --- .../tracking/impl/DynamicTrackedRaceImpl.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index bd9d548b26a..355058edb90 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -362,6 +362,48 @@ DynamicTrackedRace, GPSTrackListener { } } + private void notifyListenersWaypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + RaceChangeListener[] listeners; + synchronized (getListeners()) { + listeners = getListeners().toArray(new RaceChangeListener[getListeners().size()]); + } + for (RaceChangeListener listener : listeners) { + try { + listener.waypointAdded(zeroBasedIndex, waypointThatGotAdded); + } catch (Exception e) { + logger.log(Level.SEVERE, "RaceChangeListener " + listener + " threw exception " + e.getMessage()); + logger.log(Level.SEVERE, "notifyListenersWaypointAdded(int, Waypoint)", e); + } + } + } + + private void notifyListenersWaypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + RaceChangeListener[] listeners; + synchronized (getListeners()) { + listeners = getListeners().toArray(new RaceChangeListener[getListeners().size()]); + } + for (RaceChangeListener listener : listeners) { + try { + listener.waypointRemoved(zeroBasedIndex, waypointThatGotRemoved); + } catch (Exception e) { + logger.log(Level.SEVERE, "RaceChangeListener " + listener + " threw exception " + e.getMessage()); + logger.log(Level.SEVERE, "notifyListenersWaypointRemoved(int, Waypoint)", e); + } + } + } + + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + super.waypointAdded(zeroBasedIndex, waypointThatGotAdded); + notifyListenersWaypointAdded(zeroBasedIndex, waypointThatGotAdded); + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + super.waypointRemoved(zeroBasedIndex, waypointThatGotRemoved); + notifyListenersWaypointRemoved(zeroBasedIndex, waypointThatGotRemoved); + } + private void notifyListeners(GPSFix fix, Mark mark) { RaceChangeListener[] listeners; synchronized (getListeners()) { From bed511e75b1fd4e73ed75f7d295fc475c449b3ef Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 22:25:10 +0200 Subject: [PATCH 063/110] removed the otherwise unused BaseRaceChangeListener class and replaced its only use by AbstractRaceChangeListener --- .../test/impl/TrackedRaceLoadsFixesTest.java | 4 +- .../impl/AbstractSimpleLeaderboardImpl.java | 10 +++ .../tracking/impl/BaseRaceChangeListener.java | 64 ------------------- 3 files changed, 12 insertions(+), 66 deletions(-) delete mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BaseRaceChangeListener.java diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java index 01559572692..97fd8210cd1 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java @@ -31,7 +31,7 @@ import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifie import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRegatta; -import com.sap.sailing.domain.tracking.impl.BaseRaceChangeListener; +import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl; @@ -114,7 +114,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest { DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0); - trackedRace.addListener(new BaseRaceChangeListener() { + trackedRace.addListener(new AbstractRaceChangeListener() { boolean currentlyLoadingFirst = true; @Override public void competitorPositionChanged(GPSFixMoving fix, Competitor competitor) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 3de56864621..af99ebd7ce4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -287,6 +287,16 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race invalidateCacheAndRemoveThisListenerFromTrackedRace(); } + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + invalidateCacheAndRemoveThisListenerFromTrackedRace(); + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + invalidateCacheAndRemoveThisListenerFromTrackedRace(); + } + @Override public void statusChanged(TrackedRaceStatus newStatus) { // when the status changes away from LOADING, calculations may start or resume, making it necessary to clear the cache diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BaseRaceChangeListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BaseRaceChangeListener.java deleted file mode 100644 index a327f5c6f0d..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BaseRaceChangeListener.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.sap.sailing.domain.tracking.impl; - -import java.util.Map; - -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Mark; -import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.common.TimePoint; -import com.sap.sailing.domain.common.WindSource; -import com.sap.sailing.domain.tracking.GPSFix; -import com.sap.sailing.domain.tracking.GPSFixMoving; -import com.sap.sailing.domain.tracking.MarkPassing; -import com.sap.sailing.domain.tracking.RaceChangeListener; -import com.sap.sailing.domain.tracking.TrackedRaceStatus; -import com.sap.sailing.domain.tracking.Wind; - -public class BaseRaceChangeListener implements RaceChangeListener { - - @Override - public void competitorPositionChanged(GPSFixMoving fix, Competitor competitor) { - } - - @Override - public void markPositionChanged(GPSFix fix, Mark mark) { - } - - @Override - public void markPassingReceived(Competitor competitor, Map oldMarkPassings, - Iterable markPassings) { - } - - @Override - public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) { - } - - @Override - public void windDataReceived(Wind wind, WindSource windSource) { - } - - @Override - public void windDataRemoved(Wind wind, WindSource windSource) { - } - - @Override - public void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) { - } - - @Override - public void raceTimesChanged(TimePoint startOfTracking, TimePoint endOfTracking, TimePoint startTimeReceived) { - } - - @Override - public void delayToLiveChanged(long delayToLiveInMillis) { - } - - @Override - public void windSourcesToExcludeChanged(Iterable windSourcesToExclude) { - } - - @Override - public void statusChanged(TrackedRaceStatus newStatus) { - } - -} From 62d96329809b2eb86b8cc46714507c9a5744e807 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 22:38:12 +0200 Subject: [PATCH 064/110] made compilable by adding all necessary implementations of waypointAdded/waypointRemoved in all RaceChangeListeners --- .../domain/leaderboard/LeaderboardCacheManager.java | 10 ++++++++++ .../impl/TrackBasedEstimationWindTrackImpl.java | 10 ++++++++++ .../sailing/domain/tracking/impl/TrackedLegImpl.java | 10 ++++++++++ .../sailing/server/impl/RacingEventServiceImpl.java | 10 ++++++++++ 4 files changed, 40 insertions(+) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java index a9db6d4d6a9..4ad77b0fc01 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java @@ -103,6 +103,16 @@ public class LeaderboardCacheManager { removeFromCache(leaderboard); } + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + removeFromCache(leaderboard); + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + removeFromCache(leaderboard); + } + @Override public void delayToLiveChanged(long delayToLiveInMillis) { removeFromCache(leaderboard); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java index 94d338e81ca..02bdf90df60 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java @@ -677,6 +677,16 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl impl clearCache(); } + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + clearCache(); + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + clearCache(); + } + @Override public void markPositionChanged(GPSFix fix, Mark mark) { assert fix != null && fix.getTimePoint() != null; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java index fc4e3fb04ff..e812d76b735 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java @@ -198,6 +198,16 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener { clearCaches(); } + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + clearCaches(); // necessary because the competitor tracks ordered by rank may change for legs adjacent to the waypoint added + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + clearCaches(); // necessary because the competitor tracks ordered by rank may change for legs adjacent to the waypoint removed + } + @Override public void statusChanged(TrackedRaceStatus newStatus) { // no-op; the leg doesn't mind the tracked race's status being updated diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 59041d3c4e5..a44a3a2ff68 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -1328,6 +1328,16 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport public void raceTimesChanged(TimePoint startOfTracking, TimePoint endOfTracking, TimePoint startTimeReceived) { replicate(new UpdateRaceTimes(getRaceIdentifier(), startOfTracking, endOfTracking, startTimeReceived)); } + + @Override + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { + // no-op; the course change is replicated by the separate CourseChangeReplicator + } + + @Override + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { + // no-op; the course change is replicated by the separate CourseChangeReplicator + } @Override public void delayToLiveChanged(long delayToLiveInMillis) { From fa0ff723d33188c84c621490dc82be63c465115e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 23:53:47 +0200 Subject: [PATCH 065/110] enable CrossTrackErrorCache to clear the entire cache by using null as update interval which was so far not completely implemented --- .../tracking/impl/CrossTrackErrorCache.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java index 26232c9cad4..20c12f64114 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CrossTrackErrorCache.java @@ -85,6 +85,15 @@ public class CrossTrackErrorCache extends AbstractRaceChangeListener { private static final long serialVersionUID = 4884868659665863604L; + public void deleteAll() { + lockForWrite(); + try { + getInternalRawFixes().clear(); + } finally { + unlockAfterWrite(); + } + } + public void deleteAllLaterThan(TimePoint from) { // TODO use a specialized ArrayList in a specialized ArrayListNavigableSet and then make removeRange public lockForWrite(); @@ -171,7 +180,11 @@ public class CrossTrackErrorCache extends AbstractRaceChangeListener { FromTimePointToEndUpdateInterval updateInterval) { CrossTrackErrorSumAndNumberOfFixesTrack result; if (oldValue != null) { - oldValue.deleteAllLaterThan(updateInterval.getFrom()); + if (updateInterval == null) { + oldValue.deleteAll(); + } else { + oldValue.deleteAllLaterThan(updateInterval.getFrom()); + } computedCacheUpdate.lockForRead(); try { for (CrossTrackErrorSumAndNumberOfFixes entry : computedCacheUpdate.getRawFixes()) { @@ -446,7 +459,7 @@ public class CrossTrackErrorCache extends AbstractRaceChangeListener { final List shuffledCompetitors = new ArrayList<>(cachePerCompetitor.keySet()); Collections.shuffle(shuffledCompetitors); for (Competitor competitor : shuffledCompetitors) { - invalidate(competitor, /* from the beginning of time */ null); + cachePerCompetitor.triggerUpdate(competitor, null /* meaning: from the beginning of time */); } } From db73fa75ee9c1f0295ffa0dbfbe2d4ca79087260 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 21 Jun 2014 23:56:29 +0200 Subject: [PATCH 066/110] use single-point-of-exit pattern in CompetitorSelectionModel.getColor --- .../sap/sailing/gwt/ui/client/CompetitorSelectionModel.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java index fc5fede0d5e..819d0e29e57 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionModel.java @@ -223,15 +223,16 @@ public class CompetitorSelectionModel implements CompetitorSelectionProvider { @Override public Color getColor(CompetitorDTO competitor) { - Color result = null; + final Color result; if (allCompetitors.contains(competitor)) { if (competitor.getColor() != null) { result = competitor.getColor(); } else { result = competitorsColorMap.getColorByID(competitor); } + } else { + result = null; } - return result; } From 5a5cfea780d727c50b99d0e2b4ef8d924d62e045 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 22 Jun 2014 08:51:59 +0200 Subject: [PATCH 067/110] added FIXME comment for bug 2009 --- .../com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index abe324ac7be..ba306188b91 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -2026,6 +2026,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl GPSFixMoving next = approximationPointsIter.next(); speedWithBearingOnApproximationFromCurrentToNext = current.getSpeedAndBearingRequiredToReach(next); // compute course change on "approximation track" + // FIXME bug 2009: when a maneuver (particularly a penalty circle) is executed at high turn rates, approximations may lead to turns >180deg, hence inferred to turn the wrong way; need to loop across the non-approximated fixes here! CourseChange courseChange = speedWithBearingOnApproximationFromPreviousToCurrent .getCourseChangeRequiredToReach(speedWithBearingOnApproximationFromCurrentToNext); com.sap.sse.common.Util.Pair courseChangeAtFix = new com.sap.sse.common.Util.Pair(current, From bd6531bdae3ae290019a23144cb3fc5397df317b Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Sun, 22 Jun 2014 09:35:39 +0200 Subject: [PATCH 068/110] detect number of switches between threads loading fixes for racelog mappings for test case --- .../test/impl/TrackedRaceLoadsFixesTest.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java index 01559572692..8b8afd1273d 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java @@ -1,7 +1,6 @@ package com.sap.sailing.domain.racelogtracking.test.impl; import static junit.framework.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collections; @@ -39,7 +38,8 @@ import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl; public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest { private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er"); - private boolean finishedLoadingFirstBeforeSecond = true; + private int numberOfSwitchesBetweenLoadingRaceLogs = 0; + private Integer previouslyLoading = null; private int numFixesReceived = 0; @Test @@ -115,18 +115,15 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest { EmptyWindStore.INSTANCE, store, 0, 0, 0); trackedRace.addListener(new BaseRaceChangeListener() { - boolean currentlyLoadingFirst = true; @Override public void competitorPositionChanged(GPSFixMoving fix, Competitor competitor) { numFixesReceived++; - if (currentlyLoadingFirst) { - if (fix.getTimePoint().asMillis() > numFixes/2) { - currentlyLoadingFirst = false; - } - } else { - if (fix.getTimePoint().asMillis() <= numFixes/2) { - finishedLoadingFirstBeforeSecond = false; - } + int currentlyLoading = fix.getTimePoint().asMillis() <= numFixes/2 ? 1 : 2; + if (previouslyLoading == null) { + previouslyLoading = currentlyLoading; + } else if (previouslyLoading != currentlyLoading) { + previouslyLoading = currentlyLoading; + numberOfSwitchesBetweenLoadingRaceLogs++; } } }); @@ -137,7 +134,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest { trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog); trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog2); - assertTrue(finishedLoadingFirstBeforeSecond); + assertEquals(1, numberOfSwitchesBetweenLoadingRaceLogs); assertEquals(numFixes, numFixesReceived); } } From 60e7b713d028de7b43bf069169766c87553ed200 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sun, 22 Jun 2014 09:58:07 +0200 Subject: [PATCH 069/110] Regattas races collection is now empty when imported fixes bug2010 --- .../src/com/sap/sailing/domain/base/impl/RegattaImpl.java | 3 ++- .../src/com/sap/sailing/server/test/MasterDataImportTest.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java index 4776f9f1aee..58f7ef096b6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java @@ -62,7 +62,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene private static final Logger logger = Logger.getLogger(RegattaImpl.class.getName()); private static final long serialVersionUID = 6509564189552478869L; - private final Set races; + private Set races; private final BoatClass boatClass; private transient Set regattaListeners; private List series; @@ -185,6 +185,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene MasterDataImportInformation masterDataImportInformation = ongoingMasterDataImportInformation.get(); if (masterDataImportInformation != null) { raceLogStore = masterDataImportInformation.getRaceLogStore(); + races = new HashSet(); } else { raceLogStore = EmptyRaceLogStore.INSTANCE; } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index 04bf1039426..764b86f0ebd 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -297,6 +297,8 @@ public class MasterDataImportTest { Regatta regattaOnTarget = destService.getRegattaByName(TEST_LEADERBOARD_NAME); Assert.assertNotNull(regattaOnTarget); + Assert.assertEquals(false, regattaOnTarget.getAllRaces().iterator().hasNext()); + Assert.assertEquals(courseAreaUUID, eventOnTarget.getVenue().getCourseAreas().iterator().next().getId()); RaceColumn raceColumnOnTarget = leaderboardOnTarget.getRaceColumnByName(raceColumnName); From e2e7a3ed31ad20a00291feec57d017102a13928e Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sun, 22 Jun 2014 11:53:46 +0200 Subject: [PATCH 070/110] Added moderator shortkey CTRL+M / CTRL+m for hiding navigation panel applies to RaceboardPanel --- .../gwt/ui/raceboard/RaceBoardEntryPoint.java | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index f3e26a58565..67ab4eba18a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -5,7 +5,13 @@ import java.util.Collections; import java.util.List; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Event.NativePreviewEvent; +import com.google.gwt.user.client.Event.NativePreviewHandler; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.ServiceDefTarget; import com.google.gwt.user.client.ui.DockLayoutPanel; @@ -59,6 +65,8 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { private final MediaServiceAsync mediaService = GWT.create(MediaService.class); private final UserManagementServiceAsync userManagementService = GWT.create(UserManagementService.class); + private boolean toolbarHidden; + @Override protected void doOnModuleLoad() { super.doOnModuleLoad(); @@ -220,9 +228,9 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { private void createRaceBoardInOneScreenMode(RaceBoardPanel raceBoardPanel, RaceBoardViewConfiguration raceboardViewConfiguration) { - DockLayoutPanel p = new DockLayoutPanel(Unit.PX); + final DockLayoutPanel p = new DockLayoutPanel(Unit.PX); RootLayoutPanel.get().add(p); - Panel toolbarPanel = raceBoardPanel.getToolbarPanel(); + final Panel toolbarPanel = raceBoardPanel.getToolbarPanel(); if (!UserAgentChecker.INSTANCE.isUserAgentSupported(userAgent)) { HTML lbl = new HTML(stringMessages.warningBrowserUnsupported()); lbl.setStyleName("browserOptimizedMessage"); @@ -231,9 +239,30 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); FlowPanel timePanel = createTimePanel(raceBoardPanel); p.addNorth(logoAndTitlePanel, 68); - if (raceboardViewConfiguration.isShowNavigationPanel()) { - p.addNorth(toolbarPanel, 40); + p.addNorth(toolbarPanel, 40); + toolbarHidden = false; + if (!raceboardViewConfiguration.isShowNavigationPanel()) { + p.setWidgetHidden(toolbarPanel, true); + toolbarHidden = true; } + Event.addNativePreviewHandler(new NativePreviewHandler() { + @Override + public void onPreviewNativeEvent(NativePreviewEvent event) { + NativeEvent ne = event.getNativeEvent(); + if ("keydown".equals(ne.getType()) && ne.getCtrlKey() + && (ne.getKeyCode() == 'm' || ne.getKeyCode() == 'M')) { + ne.preventDefault(); + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + p.setWidgetHidden(toolbarPanel, !toolbarHidden); + toolbarHidden = !toolbarHidden; + } + }); + + } + } + }); p.addSouth(timePanel, 90); p.add(raceBoardPanel); p.addStyleName("dockLayoutPanel"); From 2877eb7fc8066830b5c2538ce5b109f50178c320 Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Sun, 22 Jun 2014 11:57:13 +0200 Subject: [PATCH 071/110] arrange buttons in by-name course design popup in grid instead of vertically --- ...race_choose_by_name_course_design_view.xml | 23 +++++++ .../ByNameCourseDesignDialog.java | 67 +++++++++++++------ 2 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 mobile/com.sap.sailing.racecommittee.app/res/layout/race_choose_by_name_course_design_view.xml diff --git a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_choose_by_name_course_design_view.xml b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_choose_by_name_course_design_view.xml new file mode 100644 index 00000000000..9aad5d488db --- /dev/null +++ b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_choose_by_name_course_design_view.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java index 2ad79c8b724..d60c0ec9f18 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java @@ -1,14 +1,16 @@ package com.sap.sailing.racecommittee.app.ui.fragments.dialogs.coursedesign; import android.app.Activity; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.BaseAdapter; import android.widget.Button; -import android.widget.TableLayout; -import android.widget.TableRow; +import android.widget.GridView; +import android.widget.ListAdapter; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.impl.CourseDataImpl; @@ -29,36 +31,59 @@ public class ByNameCourseDesignDialog extends RaceDialogFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - TableLayout holder = new TableLayout(getActivity()); - for (String courseName : preferences.getByNameCourseDesignerCourseNames()) { - TableRow rowLayout = new TableRow(getActivity()); - Button courseButton = new Button(getActivity()); - courseButton.setText(courseName); - courseButton.setOnClickListener( new OnClickListener() { - @Override - public void onClick(View v) { - onCourseSelectionButtonClicked(v); - } - }); - rowLayout.addView(courseButton); - holder.addView(rowLayout); - } - return holder; + return inflater.inflate(R.layout.race_choose_by_name_course_design_view, container); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - getDialog().setTitle(getString(R.string.set_course_layout)); + + GridView grid = (GridView) getView().findViewById(R.id.byNameCourseDesignGrid); + + ListAdapter buttonAdapter = new BaseAdapter() { + public int getCount() { + return preferences.getByNameCourseDesignerCourseNames().size(); + } + + public Object getItem(int position) { + return null; + } + + public long getItemId(int position) { + return 0; + } + + public View getView(int position, View convertView, ViewGroup parent) { + Button button; + if (convertView == null) { // if it's not recycled, initialize some attributes + Context ctx = ByNameCourseDesignDialog.this.getView().getContext(); + button = new Button(ctx); + button.setTextSize(ctx.getResources().getDimension(R.dimen.Welter_BigTitleTextSize)); + button.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + onCourseSelectionButtonClicked(v); + } + }); + } else { + button = (Button) convertView; + } + + button.setText(preferences.getByNameCourseDesignerCourseNames().get(position)); + return button; + } + }; + + grid.setAdapter(buttonAdapter); } - + protected void onCourseSelectionButtonClicked(View v) { Button selectedButton = (Button) v; String internalCourseName = (String) selectedButton.getText(); - + CourseBase courseLayout = new CourseDataImpl(internalCourseName); - + getRaceState().setCourseDesign(MillisecondsTimePoint.now(), courseLayout); dismiss(); } From 19ab314390b3989959f1cd6646b5d3936bd3625d Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Sun, 22 Jun 2014 12:16:08 +0200 Subject: [PATCH 072/110] Fixed NPE occurring when there is no advantage on start line --- .../src/com/sap/sailing/xmlexport/LeaderboardData.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index 49de0cce90d..c4e3b9d6189 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -30,6 +30,7 @@ import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.MaxPointsReason; +import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; @@ -227,8 +228,10 @@ public class LeaderboardData extends ExportAction { LineDetails start = race.getStartLine(race.getStartOfTracking()); addNamedElementWithValue(raceElement, "start_line_length_in_meters", start.getLength().getMeters()); - addNamedElementWithValue(raceElement, "start_advantage_in_meters", start.getAdvantage().getMeters()); - addNamedElementWithValue(raceElement, "advantageous_side_while_approaching_start_line", start.getAdvantageousSideWhileApproachingLine().name()); + Distance advantage = start.getAdvantage(); + addNamedElementWithValue(raceElement, "start_advantage_in_meters", advantage != null ? advantage.getMeters() : 0); + NauticalSide nauticalSideWhileApproaching = start.getAdvantageousSideWhileApproachingLine(); + addNamedElementWithValue(raceElement, "advantageous_side_while_approaching_start_line", nauticalSideWhileApproaching != null ? nauticalSideWhileApproaching.name() : "UNKNOWN"); Distance raceCourseLength = race.getCourseLength(); if (raceCourseLength == null) { From ac57279566043601b2a95398d22b0b75da984c0d Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Sun, 22 Jun 2014 12:23:28 +0200 Subject: [PATCH 073/110] Fixed export for data where there is no maximum speed for a competitor --- .../src/com/sap/sailing/xmlexport/ExportAction.java | 7 +++++-- .../src/com/sap/sailing/xmlexport/LeaderboardData.java | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java index 05dc44309fa..ae8b3e38399 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java @@ -34,6 +34,7 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.RacingEventService; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Triple; public abstract class ExportAction { private HttpServletRequest req; @@ -175,7 +176,8 @@ public abstract class ExportAction { return result; } - public com.sap.sse.common.Util.Triple getMaximumSpeedOverGround(Competitor competitor, TrackedRace trackedRace) { + public Triple getMaximumSpeedOverGround(Competitor competitor, TrackedRace trackedRace) { + Triple result = null; com.sap.sse.common.Util.Pair speedWithGPSFix = null; TrackedLegOfCompetitor legOfCompetitorWhereSpeedHasBeenReached = null; if (Util.contains(trackedRace.getRace().getCompetitors(), competitor)) { @@ -187,9 +189,10 @@ public abstract class ExportAction { } if (speedWithGPSFix != null) { legOfCompetitorWhereSpeedHasBeenReached = trackedRace.getTrackedLeg(competitor, speedWithGPSFix.getA().getTimePoint()); + result = new Triple(speedWithGPSFix.getA(), speedWithGPSFix.getB(), legOfCompetitorWhereSpeedHasBeenReached); } } - return new com.sap.sse.common.Util.Triple(speedWithGPSFix.getA(), speedWithGPSFix.getB(), legOfCompetitorWhereSpeedHasBeenReached); + return result; } public Speed getAverageSpeedOverGround(Leaderboard leaderboard, Competitor competitor, TimePoint timePoint, boolean alsoIncludeNonFinishedRaces) { diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index c4e3b9d6189..c54752d0540 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -438,7 +438,7 @@ public class LeaderboardData extends ExportAction { } addNamedElementWithValue(competitorRaceDataElement, "speed_when_crossing_start_line_in_knots", speedWhenCrossingStartline != null ? speedWhenCrossingStartline.getKnots() : 0); com.sap.sse.common.Util.Triple gpsFixWithSpeedAndLegInformation = getMaximumSpeedOverGround(competitor, race); - if (gpsFixWithSpeedAndLegInformation.getB() != null) { + if (gpsFixWithSpeedAndLegInformation != null && gpsFixWithSpeedAndLegInformation.getB() != null) { addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_in_knots", gpsFixWithSpeedAndLegInformation.getB().getKnots()); competitorRaceDataElement.addContent(createTimedXML("maximum_race_speed_measured_at_", gpsFixWithSpeedAndLegInformation.getA().getTimePoint())); if (gpsFixWithSpeedAndLegInformation.getC() != null) { @@ -463,6 +463,8 @@ public class LeaderboardData extends ExportAction { } else { addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_in_knots", 0.0); competitorRaceDataElement.addContent(createTimedXML("maximum_race_speed_measured_at_", race.getStartOfRace())); + addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_reached_in_leg_with_type", "UNKNOWN"); + addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_reached_in_leg", 0); raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " has no valid maximum speed for this race!", 0.5, raceConfidenceAndErrorMessages); } Distance distanceTraveledInThisRace = race.getDistanceTraveled(competitor, race.getEndOfRace()); From c280bd48f22d4de4fc2d61209b44947e55045ac3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 22 Jun 2014 12:59:02 +0200 Subject: [PATCH 074/110] making testRaceTimeReplication more robust by waiting until at least the master has received the start of tracking time; The problem here was that the abstract replication test set-up only waits until the tracked race has been received. Since the use of the TracAPI the creation of the TrackedRace object doesn't require the successful receiving of the race timings. So we need to wait for the arrival of the times separately. --- .../server/replication/test/TrackRaceReplicationTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index 8a7a9afad43..5eea659d29b 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -151,6 +151,12 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { @Test public void testRaceTimeReplication() throws InterruptedException, Exception { startTracking(); + // now wait at least until the start of tracking time has been received on the master copy + boolean receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null; + while (!receivedStartOfTracking) { // relying on the Timeout rule for this test + Thread.sleep(10); + receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null; + } Thread.sleep(2000); // kept failing several times for a 1000ms timeout TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); assertEquals(masterTrackedRace.getStartOfTracking(), replicaTrackedRace.getStartOfTracking()); From 0329bf13add13cd1c2382858f7822598a1d4dc92 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Mon, 23 Jun 2014 09:17:32 +0200 Subject: [PATCH 075/110] Test for course update LeaderboardDTO caching bug 2011 --- .../test/LeaderboardCourseChangeTest.java | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java new file mode 100644 index 00000000000..6c161164a91 --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java @@ -0,0 +1,178 @@ +package com.sap.sailing.domain.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import org.junit.Test; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.ControlPoint; +import com.sap.sailing.domain.base.Course; +import com.sap.sailing.domain.base.CourseArea; +import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.base.Series; +import com.sap.sailing.domain.base.Sideline; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.BoatClassImpl; +import com.sap.sailing.domain.base.impl.CompetitorImpl; +import com.sap.sailing.domain.base.impl.CourseImpl; +import com.sap.sailing.domain.base.impl.DomainFactoryImpl; +import com.sap.sailing.domain.base.impl.DynamicBoat; +import com.sap.sailing.domain.base.impl.DynamicTeam; +import com.sap.sailing.domain.base.impl.FleetImpl; +import com.sap.sailing.domain.base.impl.MarkImpl; +import com.sap.sailing.domain.base.impl.RegattaImpl; +import com.sap.sailing.domain.base.impl.SeriesImpl; +import com.sap.sailing.domain.base.impl.WaypointImpl; +import com.sap.sailing.domain.common.Color; +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.dto.LeaderboardDTO; +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.ScoringScheme; +import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; +import com.sap.sailing.domain.leaderboard.impl.LowPoint; +import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; +import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; +import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore; +import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.TrackedRegatta; +import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; +import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; +import com.sap.sailing.domain.tracking.impl.EmptyWindStore; + +public class LeaderboardCourseChangeTest { + + /** + * See bug 2011 + * + * @throws NoWindException + * @throws InterruptedException + * @throws ExecutionException + */ + @Test + public void testLeaderboardDTOCreationForCourseChange() throws NoWindException, InterruptedException, ExecutionException { + + Date date = Calendar.getInstance().getTime(); + TimePoint timePoint = new MillisecondsTimePoint(date); + + TrackedRegattaRegistry trackedRegattaRegistry = mock(TrackedRegattaRegistry.class); + + Fleet fleet = new FleetImpl("TestFleet"); + Series series = createSeries(trackedRegattaRegistry, fleet); + Set seriesSet = new HashSet<>(); + seriesSet.add(series); + + BoatClass boatClass = new BoatClassImpl("TestClass", true); + + String raceColumnName = "TestRace"; + RaceColumn raceColumn = series.addRaceColumn(raceColumnName, trackedRegattaRegistry); + ScoringScheme scoringScheme = new LowPoint(); + Regatta mockedRegatta = new RegattaImpl("TestRegatta", boatClass, seriesSet, false, scoringScheme, + UUID.randomUUID(), mock(CourseArea.class)); + ControlPoint start = new MarkImpl("Start"); + ControlPoint m1 = new MarkImpl("M1"); + ControlPoint m2 = new MarkImpl("M2"); + ControlPoint finish = new MarkImpl("Finish"); + Set waypoints = new HashSet<>(); + Waypoint startWaypoint = new WaypointImpl(start); + waypoints.add(startWaypoint); + Waypoint m1Waypoint1 = new WaypointImpl(m1); + Waypoint m2Waypoint1 = new WaypointImpl(m2); + Waypoint m1Waypoint2 = new WaypointImpl(m1); + Waypoint m2Waypoint2 = new WaypointImpl(m2); + Waypoint m1Waypoint3 = new WaypointImpl(m1); + waypoints.add(m1Waypoint1); + waypoints.add(m2Waypoint1); + waypoints.add(m1Waypoint2); + waypoints.add(m2Waypoint2); + waypoints.add(m1Waypoint3); + Waypoint finishWaypoint = new WaypointImpl(finish); + waypoints.add(finishWaypoint); + Course course = new CourseImpl(raceColumnName, waypoints); + TrackedRace mockedTrackedRace = createSpyedTrackedRace(mockedRegatta, course, timePoint, boatClass); + raceColumn.setTrackedRace(fleet, mockedTrackedRace); + Set raceColumnNames = new HashSet<>(); + raceColumnNames.add(raceColumnName); + + int[] ruleRaw = { 5, 3, 2 }; + ThresholdBasedResultDiscardingRule rule = new ThresholdBasedResultDiscardingRuleImpl(ruleRaw); + Leaderboard leaderboard = new RegattaLeaderboardImpl(mockedRegatta, rule); + + DomainFactory baseDomainFactory = new DomainFactoryImpl(); + LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false, + trackedRegattaRegistry, baseDomainFactory); + + assertEquals(6, leaderboardDTO.rows.values().iterator().next().fieldsByRaceColumnName.values().iterator() + .next().legDetails.size()); + + course.removeWaypoint(2); + course.removeWaypoint(3); + + leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false, trackedRegattaRegistry, + baseDomainFactory); + assertEquals(4, leaderboardDTO.rows.values().iterator().next().fieldsByRaceColumnName.values().iterator() + .next().legDetails.size()); + + } + + private TrackedRace createSpyedTrackedRace(Regatta regatta, Course course, TimePoint timePoint, BoatClass boatClass) { + TrackedRegatta mockedTrackedRegatta = createMockedTrackedRegatta(regatta); + RaceDefinition mockedRace = createMockedRace(course, boatClass); + TrackedRace spyedTrackedRace = spy(new DynamicTrackedRaceImpl(mockedTrackedRegatta, mockedRace, + new HashSet(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, 5000, 20000, 20000)); + + return spyedTrackedRace; + } + + private RaceDefinition createMockedRace(Course course, BoatClass boatClass) { + RaceDefinition mockedRaceDefinition = mock(RaceDefinition.class); + when(mockedRaceDefinition.getCompetitors()).thenReturn(new HashSet()); + when(mockedRaceDefinition.getCourse()).thenReturn(course); + when(mockedRaceDefinition.getBoatClass()).thenReturn(new BoatClassImpl("TestClass", true)); + when(mockedRaceDefinition.getName()).thenReturn("TestRace"); + Iterable competitorSet = createCompetitorSet(boatClass); + when(mockedRaceDefinition.getCompetitors()).thenReturn(competitorSet); + return mockedRaceDefinition; + } + + private Iterable createCompetitorSet(BoatClass boatClass) { + Set competitors = new HashSet<>(); + DynamicBoat mockedBoat = mock(DynamicBoat.class); + when(mockedBoat.getBoatClass()).thenReturn(boatClass); + competitors.add(new CompetitorImpl(UUID.randomUUID(), "TestCompetitor", Color.BLACK, mock(DynamicTeam.class), + mockedBoat)); + return competitors; + } + + private TrackedRegatta createMockedTrackedRegatta(Regatta regatta) { + TrackedRegatta mockedTrackedRegatta = mock(DynamicTrackedRegatta.class); + when(mockedTrackedRegatta.getRegatta()).thenReturn(regatta); + return mockedTrackedRegatta; + } + + private Series createSeries(TrackedRegattaRegistry trackedRegattaRegistry, Fleet fleet) { + Set fleets = new HashSet<>(); + fleets.add(fleet); + + Series series = new SeriesImpl("TestSeries", false, fleets, new HashSet(), trackedRegattaRegistry); + return series; + } + +} From bf01daace3cf75471a8bf2b668a0f71616830fde Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 23 Jun 2014 12:22:01 +0200 Subject: [PATCH 076/110] fixing ConcurrentModificationException in LeaderboardCacheManager by moving a modification of an inner map into a synchronized block --- .../domain/leaderboard/LeaderboardCacheManager.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java index 4ad77b0fc01..5186a20a2df 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java @@ -413,13 +413,13 @@ public class LeaderboardCacheManager { invalidationListeners = new HashMap>(); invalidationListenersPerLeaderboard.put(leaderboard, invalidationListeners); } + Set listeners = invalidationListeners.get(trackedRace); + if (listeners == null) { + listeners = new HashSet(); + invalidationListeners.put(trackedRace, listeners); + } + listeners.add(listener); } - Set listeners = invalidationListeners.get(trackedRace); - if (listeners == null) { - listeners = new HashSet(); - invalidationListeners.put(trackedRace, listeners); - } - listeners.add(listener); } From 21643e1c3291dadb2d2c7b270a296104dc5075a9 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 23 Jun 2014 13:07:03 +0200 Subject: [PATCH 077/110] use Concurrent[Weak]HashMap and Collections.synchronizedSet to generally reduce synchronization and still make iteration-safe --- .../leaderboard/LeaderboardCacheManager.java | 59 ++++++++----------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java index 5186a20a2df..89d1b797c60 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java @@ -1,10 +1,11 @@ package com.sap.sailing.domain.leaderboard; -import java.util.HashMap; +import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorChangeListener; @@ -29,6 +30,7 @@ import com.sap.sailing.domain.tracking.RaceChangeListener; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRaceStatus; import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.util.impl.ConcurrentWeakHashMap; import com.sap.sailing.util.impl.LockUtil; import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; @@ -45,7 +47,7 @@ public class LeaderboardCacheManager { private final WeakHashMap scoreCorrectionListeners; private final NamedReentrantReadWriteLock scoreCorrectionAndCompetitorChangeListenersLock; private final WeakHashMap competitorChangeListeners; - private final WeakHashMap>> invalidationListenersPerLeaderboard; + private final ConcurrentWeakHashMap>> invalidationListenersPerLeaderboard; private final WeakHashMap raceColumnListeners; private class CacheInvalidationListener implements RaceChangeListener { @@ -219,7 +221,7 @@ public class LeaderboardCacheManager { public LeaderboardCacheManager(LeaderboardCache leaderboardCache) { this.leaderboardCache = leaderboardCache; - this.invalidationListenersPerLeaderboard = new WeakHashMap<>(); + this.invalidationListenersPerLeaderboard = new ConcurrentWeakHashMap<>(); this.raceColumnListeners = new WeakHashMap(); this.scoreCorrectionListeners = new WeakHashMap<>(); this.scoreCorrectionAndCompetitorChangeListenersLock = new NamedReentrantReadWriteLock( @@ -228,11 +230,10 @@ public class LeaderboardCacheManager { } private void removeFromCache(Leaderboard leaderboard) { - synchronized (invalidationListenersPerLeaderboard) { - Map> listenersMap = invalidationListenersPerLeaderboard - .remove(leaderboard); - if (listenersMap != null) { - for (Map.Entry> e : listenersMap.entrySet()) { + ConcurrentHashMap> listenersMap = invalidationListenersPerLeaderboard.remove(leaderboard); + if (listenersMap != null) { + for (Map.Entry> e : listenersMap.entrySet()) { + synchronized (e.getValue()) { // the Set is a Collections.synchronizedSet, and synchronization is required for iteration for (CacheInvalidationListener listener : e.getValue()) { listener.removeFromTrackedRace(); } @@ -315,17 +316,7 @@ public class LeaderboardCacheManager { @Override public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) { - removeFromCache(leaderboard); - Map> listenersMap = invalidationListenersPerLeaderboard - .get(leaderboard); - if (listenersMap != null) { - Set listeners = listenersMap.get(trackedRace); - if (listeners != null) { - for (CacheInvalidationListener listener : listeners) { - listener.removeFromTrackedRace(); - } - } - } + removeFromCache(leaderboard); // removes all listeners from invalidationListenersPerLeaderboard and from their TrackedRaces } @Override @@ -403,24 +394,20 @@ public class LeaderboardCacheManager { } private void registerListener(final Leaderboard leaderboard, TrackedRace trackedRace) { - Map> invalidationListeners; + ConcurrentHashMap> invalidationListeners; final CacheInvalidationListener listener; - synchronized (invalidationListenersPerLeaderboard) { - listener = new CacheInvalidationListener(leaderboard, trackedRace); - trackedRace.addListener(listener); - invalidationListeners = invalidationListenersPerLeaderboard.get(leaderboard); - if (invalidationListeners == null) { - invalidationListeners = new HashMap>(); - invalidationListenersPerLeaderboard.put(leaderboard, invalidationListeners); - } - Set listeners = invalidationListeners.get(trackedRace); - if (listeners == null) { - listeners = new HashSet(); - invalidationListeners.put(trackedRace, listeners); - } - listeners.add(listener); + listener = new CacheInvalidationListener(leaderboard, trackedRace); + trackedRace.addListener(listener); + invalidationListeners = invalidationListenersPerLeaderboard.get(leaderboard); + if (invalidationListeners == null) { + invalidationListeners = new ConcurrentHashMap>(); + invalidationListenersPerLeaderboard.put(leaderboard, invalidationListeners); } + Set listeners = invalidationListeners.get(trackedRace); + if (listeners == null) { + listeners = Collections.synchronizedSet(new HashSet()); + invalidationListeners.put(trackedRace, listeners); + } + listeners.add(listener); } - - } From 18d43e3f191e490631c62a881f2a6e2b04c78ba4 Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Mon, 23 Jun 2014 14:13:32 +0200 Subject: [PATCH 078/110] fix NPE when attempting to log in w/o selecting role in dialog --- .../sailing/racecommittee/app/ui/activities/LoginActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java index 73f6f032dd3..f23dba56614 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java @@ -187,7 +187,6 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener public void onDialogPositiveButton(AttachedDialogFragment dialog) { // We have to take the passed instance, LoginActivity.this might be a new instance! LoginDialog localLoginDialog = (LoginDialog) dialog; - preferences.setAuthor(localLoginDialog.getAuthor()); switch (localLoginDialog.getSelectedLoginType()) { case OFFICER: ExLog.i(TAG, "Communication with backend is active."); @@ -203,6 +202,7 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener Toast.LENGTH_LONG).show(); return; } + preferences.setAuthor(localLoginDialog.getAuthor()); if (selectedCourseArea == null) { Toast.makeText(LoginActivity.this, "The selected course area was lost.", Toast.LENGTH_LONG).show(); From 23a77183e1df9e66d6737a974196ebb4ec2dd49f Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Mon, 23 Jun 2014 15:11:15 +0200 Subject: [PATCH 079/110] show date in RegattaOverview table if not today, and more dates in details footer --- .../ui/regattaoverview/RaceDetailPanel.java | 17 +++++++++--- .../RegattaRaceStatesComponent.java | 27 ++++++++++++------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java index 332ffefc1fc..5a946cbb103 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java @@ -31,7 +31,7 @@ public class RaceDetailPanel extends SimplePanel { d = -d; } NumberFormat decimalFormat = NumberFormat.getFormat("##.###"); - return "" + d + '\u00B0' + m + '\u2032' + decimalFormat.format(s) + '\u2033'; // add º, ', " symbols + return "" + d + '\u00B0' + m + '\u2032' + decimalFormat.format(s) + '\u2033'; // add �, ', " symbols } private static final int MAX_PROCEDURE_FIELDS = 2; @@ -50,7 +50,9 @@ public class RaceDetailPanel extends SimplePanel { private final Label finishTimeLabel = new Label(); private final Label finishDurationLabel = new Label(); private final Label protestTimeLabel = new Label(); + private final Label protestDateLabel = new Label(); private final Label updateTimeLabel = new Label(); + private final Label updateDateLabel = new Label(); private final Label vesselPositionLabel = new Label(); private final Label windLabel = new Label(); private final Label startProcedureLabel = new Label(); @@ -94,9 +96,9 @@ public class RaceDetailPanel extends SimplePanel { basicsGrid.setWidget(2, 1, protestTimeLabel); basicsGrid.setWidget(0, 2, startDateLabel); basicsGrid.setWidget(1, 2, finishDurationLabel); - basicsGrid.setWidget(2, 2, new Label("")); + basicsGrid.setWidget(2, 2, protestDateLabel); - Grid managementGrid = new Grid(3, 2); + Grid managementGrid = new Grid(3, 3); managementGrid.setCellSpacing(cellSpacing); managementGrid.setWidget(0, 0, new Label("Last update at")); managementGrid.setWidget(1, 0, new Label("Vessel position")); @@ -104,6 +106,7 @@ public class RaceDetailPanel extends SimplePanel { managementGrid.setWidget(0, 1, updateTimeLabel); managementGrid.setWidget(1, 1, vesselPositionLabel); managementGrid.setWidget(2, 1, windLabel); + managementGrid.setWidget(0, 2, updateDateLabel); procedureGrid = new FlexTable(); procedureGrid.setCellSpacing(cellSpacing); @@ -166,10 +169,18 @@ public class RaceDetailPanel extends SimplePanel { .format(data.raceInfo.protestFinishTime); protestTimeLabel.setText(protestTimeText); + String protestDateText = data.raceInfo.protestFinishTime == null ? "" : dateFormatter + .format(data.raceInfo.protestFinishTime); + protestDateLabel.setText(protestDateText); + String updateTimeText = data.raceInfo.lastUpdateTime == null ? "-" : timeFormatter .format(data.raceInfo.lastUpdateTime); updateTimeLabel.setText(updateTimeText); + String updateDateText = data.raceInfo.lastUpdateTime == null ? "" : dateFormatter + .format(data.raceInfo.lastUpdateTime); + updateDateLabel.setText(updateDateText); + String vesselPositionText = stringMessages.unknown(); String windText = stringMessages.unknown(); if (data.raceInfo.lastWind != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java index 2155887db0a..5f2ad99eb9a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java @@ -16,6 +16,7 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONValue; @@ -38,6 +39,7 @@ import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.datepicker.client.CalendarUtil; import com.google.gwt.view.client.ListDataProvider; import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.PassingInstruction; @@ -83,6 +85,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component private ListDataProvider regattaOverviewDataProvider; private final VerticalPanel mainPanel; private final DateTimeFormat timeFormatter = DateTimeFormat.getFormat("HH:mm:ss"); + private final DateTimeFormat dateFormatter = DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT); private final SailingServiceAsync sailingService; private final StringMessages stringMessages; @@ -207,6 +210,18 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component } })); } + + private String formatTime(Date time) { + String result = "-"; + if (time != null) { + result = ""; + if (! CalendarUtil.isSameDate(time, new Date())) { + result += dateFormatter.format(time) + " "; + } + result += timeFormatter.format(time); + } + return result; + } private CellTable createRegattaTable() { CellTable table = new CellTable(/* pageSize */10000, tableRes); @@ -341,11 +356,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component TextColumn raceStartTimeColumn = new TextColumn() { @Override public String getValue(RegattaOverviewEntryDTO entryDTO) { - String result = "-"; - if (entryDTO.raceInfo.startTime != null) { - result = timeFormatter.format(entryDTO.raceInfo.startTime); - } - return result; + return formatTime(entryDTO.raceInfo.startTime); } }; raceStartTimeColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); @@ -459,11 +470,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component TextColumn lastUpdateColumn = new TextColumn() { @Override public String getValue(RegattaOverviewEntryDTO entryDTO) { - String lastUpdateTime = "-"; - if (entryDTO.raceInfo.lastUpdateTime != null) { - lastUpdateTime = timeFormatter.format(entryDTO.raceInfo.lastUpdateTime); - } - return lastUpdateTime; + return formatTime(entryDTO.raceInfo.lastUpdateTime); } }; lastUpdateColumn.setSortable(true); From 904639a7b3b2a0bf3175ff72fb9fc0d6b98b11d6 Mon Sep 17 00:00:00 2001 From: Armin Zamani Date: Mon, 23 Jun 2014 17:37:15 +0200 Subject: [PATCH 080/110] RC App: Course Design names are displayed sortedly --- .../dialogs/coursedesign/ByNameCourseDesignDialog.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java index d60c0ec9f18..a9a9009df84 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/coursedesign/ByNameCourseDesignDialog.java @@ -1,5 +1,8 @@ package com.sap.sailing.racecommittee.app.ui.fragments.dialogs.coursedesign; +import java.util.Collections; +import java.util.List; + import android.app.Activity; import android.content.Context; import android.os.Bundle; @@ -15,6 +18,7 @@ import android.widget.ListAdapter; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.impl.CourseDataImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.impl.NaturalComparator; import com.sap.sailing.racecommittee.app.R; import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.RaceDialogFragment; @@ -40,10 +44,12 @@ public class ByNameCourseDesignDialog extends RaceDialogFragment { getDialog().setTitle(getString(R.string.set_course_layout)); GridView grid = (GridView) getView().findViewById(R.id.byNameCourseDesignGrid); + final List courses = preferences.getByNameCourseDesignerCourseNames(); + Collections.sort(courses, new NaturalComparator()); ListAdapter buttonAdapter = new BaseAdapter() { public int getCount() { - return preferences.getByNameCourseDesignerCourseNames().size(); + return courses.size(); } public Object getItem(int position) { @@ -70,7 +76,7 @@ public class ByNameCourseDesignDialog extends RaceDialogFragment { button = (Button) convertView; } - button.setText(preferences.getByNameCourseDesignerCourseNames().get(position)); + button.setText(courses.get(position)); return button; } }; From cbb21e9fa316b260caff1146e0479385518b4483 Mon Sep 17 00:00:00 2001 From: Armin Zamani Date: Mon, 23 Jun 2014 19:45:05 +0200 Subject: [PATCH 081/110] RC App: Updated app version code --- mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml b/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml index df6ef1d9ca9..1bbc1362f11 100644 --- a/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml +++ b/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="5" + android:versionName="5 - Laser" > Date: Tue, 24 Jun 2014 09:41:41 +0200 Subject: [PATCH 082/110] consolidated imports to avoid warnings --- .../gwt/home/client/place/event/TabletAndDesktopEventView.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/TabletAndDesktopEventView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/TabletAndDesktopEventView.java index 97a02c682cd..dcbaa2e00e8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/TabletAndDesktopEventView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/TabletAndDesktopEventView.java @@ -6,11 +6,9 @@ import java.util.List; import com.google.gwt.core.client.GWT; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.home.client.place.event.header.EventHeader; -import com.sap.sailing.gwt.home.client.place.event.header.EventHeaderResources; import com.sap.sailing.gwt.home.client.place.event.media.EventMedia; import com.sap.sailing.gwt.home.client.place.event.overview.EventOverview; import com.sap.sailing.gwt.home.client.place.event.regattalist.EventRegattaList; From 988c22d911e546903c89eee272a90e8a06b5bf72 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Tue, 24 Jun 2014 11:28:12 +0200 Subject: [PATCH 083/110] Global navigation controls can now be hidden in race board panel --- .../gwt/ui/raceboard/RaceBoardEntryPoint.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 67ab4eba18a..858d0d7a9b8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -65,7 +65,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { private final MediaServiceAsync mediaService = GWT.create(MediaService.class); private final UserManagementServiceAsync userManagementService = GWT.create(UserManagementService.class); - private boolean toolbarHidden; + private boolean toolbarAndLogoAndTitleBarHidden; @Override protected void doOnModuleLoad() { @@ -236,14 +236,15 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { lbl.setStyleName("browserOptimizedMessage"); toolbarPanel.add(lbl); } - FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); + final FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel); FlowPanel timePanel = createTimePanel(raceBoardPanel); p.addNorth(logoAndTitlePanel, 68); p.addNorth(toolbarPanel, 40); - toolbarHidden = false; + toolbarAndLogoAndTitleBarHidden = false; if (!raceboardViewConfiguration.isShowNavigationPanel()) { p.setWidgetHidden(toolbarPanel, true); - toolbarHidden = true; + globalNavigationPanel.setVisible(false); + toolbarAndLogoAndTitleBarHidden = true; } Event.addNativePreviewHandler(new NativePreviewHandler() { @Override @@ -255,8 +256,9 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { - p.setWidgetHidden(toolbarPanel, !toolbarHidden); - toolbarHidden = !toolbarHidden; + p.setWidgetHidden(toolbarPanel, !toolbarAndLogoAndTitleBarHidden); + globalNavigationPanel.setVisible(toolbarAndLogoAndTitleBarHidden); + toolbarAndLogoAndTitleBarHidden = !toolbarAndLogoAndTitleBarHidden; } }); From 26b41ae9ec6458963dd3dd6d0e68b9d37fb0cf9a Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 24 Jun 2014 12:37:18 +0200 Subject: [PATCH 084/110] better logging for issues with declination service during heading correction in Igtimi receiver; catch and log exception per Igtimi listener --- .../igtimiadapter/shared/IgtimiWindReceiver.java | 10 ++++++++-- .../websocket/WebSocketConnectionManager.java | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/shared/IgtimiWindReceiver.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/shared/IgtimiWindReceiver.java index a9a30bd96f5..f0f4b765eb9 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/shared/IgtimiWindReceiver.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/shared/IgtimiWindReceiver.java @@ -378,8 +378,14 @@ public class IgtimiWindReceiver implements BulkFixReceiver { if (declinationService == null) { trueHeading = hdgm; } else { - Declination declination = declinationService.getDeclination(timePoint, position, /* timeoutForOnlineFetchInMilliseconds 5s */ 5000); - trueHeading = hdgm.add(declination.getBearingCorrectedTo(timePoint)); + try { + Declination declination = declinationService.getDeclination(timePoint, position, /* timeoutForOnlineFetchInMilliseconds 5s */ 5000); + trueHeading = hdgm.add(declination.getBearingCorrectedTo(timePoint)); + } catch (Exception e) { + logger.log(Level.SEVERE, "Correction of declination was requested but unsuccessful. Can't correct heading "+ + hdgm+"@"+timePoint+" by declination. Forwarding exception."); + throw e; + } } } else { trueHeading = null; diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java index 945d5ac68d2..45f0fd54f7b 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java @@ -154,7 +154,11 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live private void notifyListeners(List fixes) { for (BulkFixReceiver listener : listeners.keySet()) { - listener.received(fixes); + try { + listener.received(fixes); + } catch (Exception e) { + logger.log(Level.SEVERE, "Error notifying listener "+listener+" of Igtimi fixes "+fixes, e); + } } } From 4f26add4034b1e3643cd710b9a03abd85422ebaf Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Tue, 24 Jun 2014 14:41:57 +0200 Subject: [PATCH 085/110] use SharedDomainFactory in deserializer --- .../impl/RaceLogDenoteForTrackingEventDeserializer.java | 9 ++++++--- .../racelog/impl/RaceLogEventDeserializer.java | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDenoteForTrackingEventDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDenoteForTrackingEventDeserializer.java index 333ed133c6e..b4c24b1cc10 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDenoteForTrackingEventDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDenoteForTrackingEventDeserializer.java @@ -7,7 +7,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogEventAuthor; @@ -16,15 +16,18 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogDenoteForTrackingEventSerializer; public class RaceLogDenoteForTrackingEventDeserializer extends BaseRaceLogEventDeserializer { - public RaceLogDenoteForTrackingEventDeserializer(JsonDeserializer competitorDeserializer) { + private final SharedDomainFactory domainFactory; + public RaceLogDenoteForTrackingEventDeserializer(JsonDeserializer competitorDeserializer, + SharedDomainFactory factory) { super(competitorDeserializer); + this.domainFactory = factory; } @Override protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt, RaceLogEventAuthor author, TimePoint timePoint, int passId, List competitors) throws JsonDeserializationException { String raceName = (String) object.get(RaceLogDenoteForTrackingEventSerializer.FIELD_RACE_NAME); - BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass( + BoatClass boatClass = domainFactory.getOrCreateBoatClass( (String) object.get(RaceLogDenoteForTrackingEventSerializer.FIELD_BOAT_CLASS)); Serializable raceId = (Serializable) object.get(RaceLogDenoteForTrackingEventSerializer.FIELD_RACE_ID); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java index a0c373c1cb5..f7bba85e088 100755 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogEventDeserializer.java @@ -75,7 +75,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer new PositionJsonDeserializer())), new RaceLogDeviceCompetitorMappingEventDeserializer(competitorDeserializer, deviceDeserializer), new RaceLogDeviceMarkMappingEventDeserializer(competitorDeserializer, new MarkDeserializer(domainFactory), deviceDeserializer), - new RaceLogDenoteForTrackingEventDeserializer(competitorDeserializer), + new RaceLogDenoteForTrackingEventDeserializer(competitorDeserializer, domainFactory), new RaceLogStartTrackingEventDeserializer(competitorDeserializer), new RaceLogRevokeEventDeserializer(competitorDeserializer), new RaceLogRegisterCompetitorEventDeserializer(competitorDeserializer), From f6653453f8784f5a2697bd7d3700985ae737d3dc Mon Sep 17 00:00:00 2001 From: fmittag Date: Tue, 24 Jun 2014 15:26:52 +0200 Subject: [PATCH 086/110] added 'startOfRace-ms' to the competitor/legs api service for the start analysis --- .../sap/sailing/server/gateway/jaxrs/api/RegattasResource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index d3d78852d7a..8fc4dc2efeb 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -566,6 +566,7 @@ public class RegattasResource extends AbstractSailingServerResource { JSONObject jsonRaceResults = new JSONObject(); jsonRaceResults.put("name", trackedRace.getRace().getName()); jsonRaceResults.put("regatta", regatta.getName()); + jsonRaceResults.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asMillis()); JSONArray jsonLegs = new JSONArray(); for (TrackedLeg leg : trackedRace.getTrackedLegs()) { From c2036cae1de457b65afba3a326309502b5e985c1 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 24 Jun 2014 15:51:24 +0200 Subject: [PATCH 087/110] aftermath for 1fe999d29988a3aee89691a8fc4090de79b42f4c, also waiting for end of tracking time --- .../server/replication/test/TrackRaceReplicationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index 5eea659d29b..8318caee327 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -155,7 +155,8 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { boolean receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null; while (!receivedStartOfTracking) { // relying on the Timeout rule for this test Thread.sleep(10); - receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null; + receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null && + master.getTrackedRace(raceIdentifier).getEndOfTracking() != null; } Thread.sleep(2000); // kept failing several times for a 1000ms timeout TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); From a14c2e18ba99ecb1d59a39194da347540155f531 Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Tue, 24 Jun 2014 17:16:43 +0200 Subject: [PATCH 088/110] RCApp: change logic for determining flags to display in RegattaOverview - compare previous FlagPoleState with current FlagPoleState - then derive what to display (flag with or without arrow) depending on delta between states --- .../domain/common/racelog/FlagPole.java | 5 + .../impl/IndividualRecallDisplayedFinder.java | 10 +- .../impl/IndividualRecallRemovedFinder.java | 11 +- .../IsIndividualRecallDisplayedAnalyzer.java | 8 +- .../state/racingprocedure/FlagPoleState.java | 114 ++++++++++++++---- .../ReadonlyRacingProcedure.java | 10 ++ .../basic/impl/BasicRacingProcedureImpl.java | 2 +- .../ess/impl/ESSRacingProcedureImpl.java | 20 +-- .../impl/GateStartRacingProcedureImpl.java | 17 +-- .../impl/BaseRacingProcedure.java | 41 +++++-- .../rrs26/impl/RRS26RacingProcedureImpl.java | 18 ++- .../ui/regattaoverview/FlagImageResolver.java | 18 +-- .../ui/regattaoverview/RaceDetailPanel.java | 2 +- .../RegattaRaceStatesComponent.java | 13 +- .../regattaoverview/SailingFlagsBuilder.java | 10 +- .../gwt/ui/server/SailingServiceImpl.java | 21 ++-- .../sailing/gwt/ui/shared/RaceInfoDTO.java | 3 +- .../raceinfo/BaseRaceInfoRaceFragment.java | 11 +- 18 files changed, 241 insertions(+), 93 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/FlagPole.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/FlagPole.java index 6da59354803..e1a9c3f28f1 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/FlagPole.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/FlagPole.java @@ -34,4 +34,9 @@ public class FlagPole { this.isDisplayed == otherPole.isDisplayed; } + public boolean describesSameButForDisplayed(FlagPole otherPole) { + return this.upperFlag == otherPole.upperFlag && + this.lowerFlag == otherPole.lowerFlag; + } + } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallDisplayedFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallDisplayedFinder.java index de3ed99ca6d..6e2c38f6634 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallDisplayedFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallDisplayedFinder.java @@ -1,17 +1,25 @@ package com.sap.sailing.domain.racelog.analyzing.impl; +import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLogFlagEvent; public class IndividualRecallDisplayedFinder extends IndividualRecallFinder { + private final TimePoint at; public IndividualRecallDisplayedFinder(RaceLog raceLog) { + this(raceLog, null); + } + + public IndividualRecallDisplayedFinder(RaceLog raceLog, TimePoint at) { super(raceLog); + this.at = at; } protected boolean isRelevant(RaceLogFlagEvent flagEvent) { - return flagEvent.getUpperFlag().equals(Flags.XRAY) && flagEvent.isDisplayed(); + boolean disregardBecauseAfterAt = at != null && flagEvent.getTimePoint().after(at); + return ! disregardBecauseAfterAt && flagEvent.getUpperFlag().equals(Flags.XRAY) && flagEvent.isDisplayed(); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallRemovedFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallRemovedFinder.java index 76ce775ea48..f92d9bb3f54 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallRemovedFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IndividualRecallRemovedFinder.java @@ -1,17 +1,26 @@ package com.sap.sailing.domain.racelog.analyzing.impl; +import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLogFlagEvent; public class IndividualRecallRemovedFinder extends IndividualRecallFinder { + private final TimePoint at; + public IndividualRecallRemovedFinder(RaceLog raceLog) { + this(raceLog, null); + } + + public IndividualRecallRemovedFinder(RaceLog raceLog, TimePoint at) { super(raceLog); + this.at = at; } protected boolean isRelevant(RaceLogFlagEvent flagEvent) { - return flagEvent.getUpperFlag().equals(Flags.XRAY) && !flagEvent.isDisplayed(); + boolean disregardBecauseAfterAt = at != null && flagEvent.getTimePoint().after(at); + return ! disregardBecauseAfterAt && flagEvent.getUpperFlag().equals(Flags.XRAY) && !flagEvent.isDisplayed(); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IsIndividualRecallDisplayedAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IsIndividualRecallDisplayedAnalyzer.java index 3c880386a6c..785a242ad21 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IsIndividualRecallDisplayedAnalyzer.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/analyzing/impl/IsIndividualRecallDisplayedAnalyzer.java @@ -9,11 +9,15 @@ public class IsIndividualRecallDisplayedAnalyzer extends RaceLogAnalyzer currentState; private final List nextState; + private final TimePoint currentStateValidFrom; private final TimePoint nextStateValidFrom; private final boolean hasNextState; - public FlagPoleState(List currentState) { - this(currentState, null, null, false); + public FlagPoleState(List currentState, TimePoint currentStateValidFrom) { + this(currentState, currentStateValidFrom, null, null, false); } - public FlagPoleState(List currentState, List nextState, TimePoint nextStateValidFrom) { - this(currentState, nextState, nextStateValidFrom, true); + public FlagPoleState(List currentState, TimePoint currentStateValidFrom, + List nextState, TimePoint nextStateValidFrom) { + this(currentState, currentStateValidFrom, nextState, nextStateValidFrom, true); } - private FlagPoleState(List currentState, List nextState, TimePoint nextStateValidFrom, boolean hasNextState) { + private FlagPoleState(List currentState, TimePoint currentStateValidFrom, + List nextState, TimePoint nextStateValidFrom, boolean hasNextState) { this.currentState = currentState; + this.currentStateValidFrom = currentStateValidFrom; this.nextState = nextState; this.nextStateValidFrom = nextStateValidFrom; this.hasNextState = hasNextState; @@ -42,30 +46,51 @@ public class FlagPoleState { public TimePoint getNextStateValidFrom() { return nextStateValidFrom; } + + public TimePoint getCurrentStateValidFrom() { + return currentStateValidFrom; + } public boolean hasNextState() { return hasNextState; } + public static List computeChanges(List before, List after) { + before = before == null ? Collections.emptyList() : before; + after = after == null ? Collections.emptyList() : after; + + List beforeWithoutMatchInAfter = new ArrayList(before); + List changes = new ArrayList(); + for (FlagPole poleAfter : after) { + boolean hasSamePoleBefore = false; + for (FlagPole poleBefore : before) { + if (poleAfter.describesSameButForDisplayed(poleBefore)) { + beforeWithoutMatchInAfter.remove(poleBefore); + } + if (poleAfter.describesSame(poleBefore)) { + hasSamePoleBefore = true; + break; + } + } + if (!hasSamePoleBefore) { + changes.add(poleAfter); + } + } + + for (FlagPole poleBefore : beforeWithoutMatchInAfter) { + if (poleBefore.isDisplayed()) { + changes.add(new FlagPole(poleBefore.getUpperFlag(), poleBefore.getLowerFlag(), false)); + } + } + return changes; + } + public List computeUpcomingChanges() { if (!hasNextState) { return Collections.emptyList(); } - List changes = new ArrayList(); - for (FlagPole nextPole : nextState) { - boolean hasSamePoleInCurrentState = false; - for (FlagPole currentPole : currentState) { - if (nextPole.describesSame(currentPole)) { - hasSamePoleInCurrentState = true; - break; - } - } - if (!hasSamePoleInCurrentState) { - changes.add(nextPole); - } - } - return changes; + return computeChanges(currentState, nextState); } public static boolean describesSameState(FlagPoleState left, FlagPoleState right) { @@ -99,5 +124,52 @@ public class FlagPoleState { return true; } - + public FlagPoleState getPreviousState(ReadonlyRacingProcedure racingProcedure, TimePoint startTime) { + if (currentStateValidFrom == null) { + return this; + } + return racingProcedure.getActiveFlags(startTime, currentStateValidFrom.minus(1)); + } + + public static FlagPole getMostInterestingFlagPole(List poles) { + return getMostInterestingFlagPole(poles, poles); + } + + public static FlagPole getMostInterestingFlagPole(List previousPoles, List currentPoles) { + List changes = computeChanges(previousPoles, currentPoles); + FlagPole poleRemoved = null; + for (FlagPole changedPole : changes) { + if (changedPole.isDisplayed()) { + return changedPole; + } else { + poleRemoved = changedPole; + } + } + if (poleRemoved != null) { + return poleRemoved; + } + + for (FlagPole currentPole : currentPoles) { + if (currentPole.isDisplayed()) { + return currentPole; + } + } + return currentPoles.size() == 0 ? null : currentPoles.get(0); + } + + public boolean hasPoleChanged(FlagPole newPole) { + boolean found = false; + for (FlagPole current : currentState) { + if (current.describesSameButForDisplayed(newPole)) { + found = true; + if (current.isDisplayed() != newPole.isDisplayed()) { + return true; + } + } + } + if (! found && newPole.isDisplayed()) { + return true; + } + return false; + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ReadonlyRacingProcedure.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ReadonlyRacingProcedure.java index ffc4e71bbc3..e84c6eaacba 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ReadonlyRacingProcedure.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ReadonlyRacingProcedure.java @@ -68,6 +68,16 @@ public interface ReadonlyRacingProcedure extends RaceStateEventProcessor, RaceSt */ boolean isIndividualRecallDisplayed(); + /** + * Returns true if there is an individual recall signaled in the {@link RaceLog} at the TimePoint {@code at}. + */ + boolean isIndividualRecallDisplayed(TimePoint at); + + /** + * Gets the time the individual recall flag was displayed (or null). + */ + TimePoint getIndividualRecallDisplayedTime(); + /** * Gets the time the individual recall flag was removed (or null). */ diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/basic/impl/BasicRacingProcedureImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/basic/impl/BasicRacingProcedureImpl.java index ddd92d664a4..45055dba2dc 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/basic/impl/BasicRacingProcedureImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/basic/impl/BasicRacingProcedureImpl.java @@ -47,7 +47,7 @@ public class BasicRacingProcedureImpl extends BaseRacingProcedure { @Override public FlagPoleState getActiveFlags(TimePoint startTime, TimePoint now) { - return new FlagPoleState(Collections.emptyList()); + return new FlagPoleState(Collections.emptyList(), null); } @Override diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ess/impl/ESSRacingProcedureImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ess/impl/ESSRacingProcedureImpl.java index dea4ca396f6..e97996ee46d 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ess/impl/ESSRacingProcedureImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/ess/impl/ESSRacingProcedureImpl.java @@ -91,37 +91,43 @@ public class ESSRacingProcedureImpl extends BaseRacingProcedure implements ESSRa public FlagPoleState getActiveFlags(TimePoint startTime, TimePoint now) { if (now.before(startTime.minus(startPhaseAPDownInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(Flags.AP, true), new FlagPole(Flags.ESSTHREE, false)), + Arrays.asList(new FlagPole(Flags.AP, true), new FlagPole(Flags.ESSTHREE, false)), null, Arrays.asList(new FlagPole(Flags.AP, false), new FlagPole(Flags.ESSTHREE, false)), startTime.minus(startPhaseAPDownInterval)); } else if (now.before(startTime.minus(startPhaseESSThreeUpInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(Flags.AP, false), new FlagPole(Flags.ESSTHREE, false)), + Arrays.asList(new FlagPole(Flags.AP, false), new FlagPole(Flags.ESSTHREE, false)), + startTime.minus(startPhaseAPDownInterval), Arrays.asList(new FlagPole(Flags.ESSTHREE, true), new FlagPole(Flags.ESSTWO, false)), startTime.minus(startPhaseESSThreeUpInterval)); } else if (now.before(startTime.minus(startPhaseESSTwoUpInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(Flags.ESSTHREE, true), new FlagPole(Flags.ESSTWO, false)), + Arrays.asList(new FlagPole(Flags.ESSTHREE, true), new FlagPole(Flags.ESSTWO, false)), + startTime.minus(startPhaseESSThreeUpInterval), Arrays.asList(new FlagPole(Flags.ESSTWO, true), new FlagPole(Flags.ESSONE, false)), startTime.minus(startPhaseESSTwoUpInterval)); } else if (now.before(startTime.minus(startPhaseESSOneUpInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(Flags.ESSTWO, true), new FlagPole(Flags.ESSONE, false)), + Arrays.asList(new FlagPole(Flags.ESSTWO, true), new FlagPole(Flags.ESSONE, false)), + startTime.minus(startPhaseESSTwoUpInterval), Arrays.asList(new FlagPole(Flags.ESSONE, true)), startTime.minus(startPhaseESSOneUpInterval)); } if (now.before(startTime)) { return new FlagPoleState( - Arrays.asList(new FlagPole(Flags.ESSONE, true)), + Arrays.asList(new FlagPole(Flags.ESSONE, true)), + startTime.minus(startPhaseESSOneUpInterval), Arrays.asList(new FlagPole(Flags.ESSONE, false)), startTime); } else { - if (isIndividualRecallDisplayed()) { + if (isIndividualRecallDisplayed(now)) { return new FlagPoleState( Arrays.asList(new FlagPole(Flags.XRAY, true)), + getIndividualRecallDisplayedTime(), Arrays.asList(new FlagPole(Flags.XRAY, false)), getIndividualRecallRemovalTime()); } else { - return new FlagPoleState(Collections.emptyList()); + TimePoint recallRemoved = getIndividualRecallRemovalTime(); + return new FlagPoleState(Collections.emptyList(), recallRemoved == null ? startTime : recallRemoved); } } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/gate/impl/GateStartRacingProcedureImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/gate/impl/GateStartRacingProcedureImpl.java index 730c043ec69..4329f26b46b 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/gate/impl/GateStartRacingProcedureImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/gate/impl/GateStartRacingProcedureImpl.java @@ -146,24 +146,27 @@ public class GateStartRacingProcedureImpl extends BaseRacingProcedure implements TimePoint gateShutdownTime = getGateShutdownTimePoint(startTime); if (now.before(startTime.minus(startPhaseClassOverGolfUpIntervall))) { return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, false), new FlagPole(Flags.PAPA, - false)), Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)), + false)), null, Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)), startTime.minus(startPhaseClassOverGolfUpIntervall)); } else if (now.before(startTime.minus(startPhasePapaUpInterval))) { return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, - false)), Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, true)), + false)), startTime.minus(startPhaseClassOverGolfUpIntervall), + Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, true)), startTime.minus(startPhasePapaUpInterval)); } else if (now.before(startTime.minus(startPhasePapaDownInterval))) { return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, - true)), Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)), + true)), startTime.minus(startPhasePapaUpInterval), + Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)), startTime.minus(startPhasePapaDownInterval)); } else if (now.before(startTime)) { return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, - false)), Arrays.asList(new FlagPole(classFlag, false), new FlagPole(Flags.GOLF, true)), startTime); + false)), startTime.minus(startPhasePapaDownInterval), + Arrays.asList(new FlagPole(classFlag, false), new FlagPole(Flags.GOLF, true)), startTime); } else if (now.before(gateShutdownTime)) { - return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, true)), Arrays.asList(new FlagPole( - Flags.GOLF, false)), gateShutdownTime); + return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, true)), startTime, + Arrays.asList(new FlagPole(Flags.GOLF, false)), gateShutdownTime); } else { - return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, false))); + return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, false)), gateShutdownTime); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java index 956d94bb26c..223a940f966 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/impl/BaseRacingProcedure.java @@ -13,6 +13,7 @@ import com.sap.sailing.domain.racelog.RaceLogEventAuthor; import com.sap.sailing.domain.racelog.RaceLogEventFactory; import com.sap.sailing.domain.racelog.RaceLogEventVisitor; import com.sap.sailing.domain.racelog.analyzing.impl.IndividualRecallDisplayedFinder; +import com.sap.sailing.domain.racelog.analyzing.impl.IndividualRecallRemovedFinder; import com.sap.sailing.domain.racelog.analyzing.impl.IsIndividualRecallDisplayedAnalyzer; import com.sap.sailing.domain.racelog.impl.RaceLogChangedVisitor; import com.sap.sailing.domain.racelog.state.RaceStateEvent; @@ -46,6 +47,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i private final RacingProcedureChangedListeners changedListeners; private final IsIndividualRecallDisplayedAnalyzer isRecallDisplayedAnalyzer; private final IndividualRecallDisplayedFinder recallDisplayedFinder; + private final IndividualRecallRemovedFinder recallRemovedFinder; private final RaceLogEventVisitor raceLogListener; private boolean cachedIsIndividualRecallDisplayed; @@ -67,6 +69,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i this.changedListeners = createChangedListenerContainer(); this.isRecallDisplayedAnalyzer = new IsIndividualRecallDisplayedAnalyzer(raceLog); this.recallDisplayedFinder = new IndividualRecallDisplayedFinder(raceLog); + this.recallRemovedFinder = new IndividualRecallRemovedFinder(raceLog); this.raceLogListener = new RaceLogChangedVisitor(this); this.raceLog.addListener(raceLogListener); @@ -126,17 +129,34 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i public boolean isIndividualRecallDisplayed() { return cachedIsIndividualRecallDisplayed; } + + @Override + public boolean isIndividualRecallDisplayed(TimePoint at) { + if (hasIndividualRecall()) { + return new IsIndividualRecallDisplayedAnalyzer(getRaceLog(), at).analyze(); + } + return false; + } + + @Override + public TimePoint getIndividualRecallDisplayedTime() { + if (hasIndividualRecall()) { + return recallDisplayedFinder.analyze(); + } + return null; + } @Override public TimePoint getIndividualRecallRemovalTime() { + TimePoint displayed = getIndividualRecallDisplayedTime(); if (hasIndividualRecall()) { - if (isIndividualRecallDisplayed()) { - TimePoint displayTime = recallDisplayedFinder.analyze(); - if (displayTime != null) { - return displayTime.plus(individualRecallRemovalTimeout); - } + TimePoint removedEvent = recallRemovedFinder.analyze(); + if (removedEvent != null && (displayed == null || removedEvent.after(displayed))) { + return removedEvent; } - + } + if (displayed != null) { + return displayed.plus(individualRecallRemovalTimeout); } return null; } @@ -177,10 +197,6 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i protected void update() { boolean isRecallDisplayed = isRecallDisplayedAnalyzer.analyze(); - // always call listeners for changed flag, as this does not only affect recal, but also - // changes start procedure, for which some text elements need to be updated - // ({@link BaseRaceInfoFragmen#renderFlagChangesCountdown} - changedListeners.onActiveFlagsChanged(this); if (cachedIsIndividualRecallDisplayed != isRecallDisplayed) { cachedIsIndividualRecallDisplayed = isRecallDisplayed; @@ -192,6 +208,11 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT); } } + + // always call listeners for changed flag, as this does not only affect recal, but also + // changes start procedure, for which some text elements need to be updated + // ({@link BaseRaceInfoFragmen#renderFlagChangesCountdown} + changedListeners.onActiveFlagsChanged(this); } private void rescheduleIndividualRecallTimeout(TimePoint displayTime) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/rrs26/impl/RRS26RacingProcedureImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/rrs26/impl/RRS26RacingProcedureImpl.java index 5089870d47c..3bb9af9d989 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/rrs26/impl/RRS26RacingProcedureImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/state/racingprocedure/rrs26/impl/RRS26RacingProcedureImpl.java @@ -126,32 +126,38 @@ public class RRS26RacingProcedureImpl extends BaseRacingProcedure implements RRS getConfiguration().getClassFlag() : Flags.CLASS; if (now.before(startTime.minus(startPhaseClassUpInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(classFlag, false), new FlagPole(cachedStartmodeFlag, false)), + Arrays.asList(new FlagPole(classFlag, false), new FlagPole(cachedStartmodeFlag, false)), + null, Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)), startTime.minus(startPhaseClassUpInterval)); } else if (now.before(startTime.minus(startPhaseStartModeUpInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)), + Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)), + startTime.minus(startPhaseClassUpInterval), Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, true)), startTime.minus(startPhaseStartModeUpInterval)); } else if (now.before(startTime.minus(startPhaseStartModeDownInterval))) { return new FlagPoleState( - Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, true)), + Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, true)), + startTime.minus(startPhaseStartModeUpInterval), Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)), startTime.minus(startPhaseStartModeDownInterval)); } else if (now.before(startTime)) { return new FlagPoleState( - Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)), + Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)), + startTime.minus(startPhaseStartModeDownInterval), Arrays.asList(new FlagPole(classFlag, false), new FlagPole(cachedStartmodeFlag, false)), startTime); } else { - if (isIndividualRecallDisplayed()) { + if (isIndividualRecallDisplayed(now)) { return new FlagPoleState( Arrays.asList(new FlagPole(Flags.XRAY, true)), + getIndividualRecallDisplayedTime(), Arrays.asList(new FlagPole(Flags.XRAY, false)), getIndividualRecallRemovalTime()); } else { - return new FlagPoleState(Collections.emptyList()); + TimePoint recallRemoved = getIndividualRecallRemovalTime(); + return new FlagPoleState(Collections.emptyList(), recallRemoved == null ? startTime : recallRemoved); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/FlagImageResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/FlagImageResolver.java index c6c7435217a..ec4cfa5a2a3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/FlagImageResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/FlagImageResolver.java @@ -38,17 +38,19 @@ public class FlagImageResolver { flagImageMap.put(Flags.ZULU, resources.flagZulu()); } - public ImageResource resolveFlagToImage(Flags flag) { - return flagImageMap.get(flag); + public ImageResource resolveFlagToImage(Flags flag, boolean isDisplayed, boolean displayStateChanged) { + return isDisplayed || displayStateChanged ? flagImageMap.get(flag) : null; } - public ImageResource resolveFlagDirectionToImage(boolean isDisplayed) { - ImageResource result; + public ImageResource resolveFlagDirectionToImage(boolean isDisplayed, boolean displayStateChanged) { + ImageResource result = null; - if (isDisplayed) { - result = resources.arrowUp(); - } else { - result = resources.arrowDown(); + if (displayStateChanged) { + if (isDisplayed) { + result = resources.arrowUp(); + } else { + result = resources.arrowDown(); + } } return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java index 5a946cbb103..627c9f46675 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RaceDetailPanel.java @@ -141,7 +141,7 @@ public class RaceDetailPanel extends SimplePanel { return; } String statusText = flagInterpreter.getMeaningOfRaceStateAndFlags(data.raceInfo.lastStatus, - data.raceInfo.lastUpperFlag, data.raceInfo.lastLowerFlag, data.raceInfo.isLastFlagDisplayed); + data.raceInfo.lastUpperFlag, data.raceInfo.lastLowerFlag, data.raceInfo.lastFlagsAreDisplayed); String headerLabelText = data.regattaDisplayName + " " + data.raceInfo.fleetName + " " + data.raceInfo.raceName + " (" + statusText + ")"; headerLabel.setText(stringMessages.showingDetailsOfRace(headerLabelText)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java index 5f2ad99eb9a..5ba9e2b3644 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaRaceStatesComponent.java @@ -89,6 +89,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component private final SailingServiceAsync sailingService; private final StringMessages stringMessages; + private final ErrorReporter errorReporter; private final UUID eventId; private EventDTO eventDTO; @@ -129,6 +130,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component final StringMessages stringMessages, final UUID eventId, RegattaRaceStatesSettings settings, Timer timerToSynchronize) { this.sailingService = sailingService; this.stringMessages = stringMessages; + this.errorReporter = errorReporter; this.eventId = eventId; this.allEntries = new ArrayList(); this.timerToSynchronize = timerToSynchronize; @@ -193,7 +195,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component new AsyncCallback>() { @Override public void onFailure(Throwable cause) { - + errorReporter.reportError(cause.getMessage()); } @Override @@ -386,7 +388,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component @Override public SafeHtml getValue(final RegattaOverviewEntryDTO entryDTO) { String status = flagInterpreter.getMeaningOfRaceStateAndFlags(entryDTO.raceInfo.lastStatus, entryDTO.raceInfo.lastUpperFlag, - entryDTO.raceInfo.lastLowerFlag, entryDTO.raceInfo.isLastFlagDisplayed); + entryDTO.raceInfo.lastLowerFlag, entryDTO.raceInfo.lastFlagsAreDisplayed); SafeHtmlBuilder sb = new SafeHtmlBuilder(); sb.appendHtmlConstant(""); sb.appendEscaped(status); @@ -455,12 +457,13 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component tooltip += " " + FlagsMeaningExplanator.getFlagsMeaning(stringMessages, entryDTO.raceInfo.lastUpperFlag, entryDTO.raceInfo.lastLowerFlag, - entryDTO.raceInfo.isLastFlagDisplayed); + entryDTO.raceInfo.lastFlagsAreDisplayed); } return SailingFlagsBuilder.render( entryDTO.raceInfo.lastUpperFlag, entryDTO.raceInfo.lastLowerFlag, - entryDTO.raceInfo.isLastFlagDisplayed, + entryDTO.raceInfo.lastFlagsAreDisplayed, + entryDTO.raceInfo.lastFlagsDisplayedStateChanged, tooltip); } @@ -493,7 +496,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component } else if (entryDTO.raceInfo.lastLowerFlag != null && entryDTO.raceInfo.lastLowerFlag.equals(Flags.HOTEL)) { additionalInformation.append(stringMessages.furtherSignalsAshore()); isInfoBefore = true; - } else if (entryDTO.raceInfo.lastUpperFlag != null && entryDTO.raceInfo.lastUpperFlag.equals(Flags.XRAY) && entryDTO.raceInfo.isLastFlagDisplayed) { + } else if (entryDTO.raceInfo.lastUpperFlag != null && entryDTO.raceInfo.lastUpperFlag.equals(Flags.XRAY) && entryDTO.raceInfo.lastFlagsAreDisplayed) { additionalInformation.append(stringMessages.earlyStarters()); isInfoBefore = true; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/SailingFlagsBuilder.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/SailingFlagsBuilder.java index 522f1bd7c01..b76ea5c2215 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/SailingFlagsBuilder.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/SailingFlagsBuilder.java @@ -40,15 +40,15 @@ public class SailingFlagsBuilder { return SafeStylesUtils.fromTrustedString("float:left;padding-right:10px;"); } - public static SafeHtml render(Flags upperFlag, Flags lowerFlag, boolean isDisplayed, String tooltip) { + public static SafeHtml render(Flags upperFlag, Flags lowerFlag, boolean isDisplayed, boolean displayStateChanged, String tooltip) { FlagImageResolver flagImageResolver = new FlagImageResolver(); - ImageResource upperFlagImage = flagImageResolver.resolveFlagToImage(upperFlag); - ImageResource lowerFlagImage = flagImageResolver.resolveFlagToImage(lowerFlag); - ImageResource directionImage = flagImageResolver.resolveFlagDirectionToImage(isDisplayed); + ImageResource upperFlagImage = flagImageResolver.resolveFlagToImage(upperFlag, isDisplayed, displayStateChanged); + ImageResource lowerFlagImage = flagImageResolver.resolveFlagToImage(lowerFlag, isDisplayed, displayStateChanged); + ImageResource directionImage = flagImageResolver.resolveFlagDirectionToImage(isDisplayed, displayStateChanged); SafeHtml upperFlagHtml = (upperFlagImage != null) ? makeImagePrototype(upperFlagImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML; SafeHtml lowerFlagHtml = (lowerFlagImage != null) ? makeImagePrototype(lowerFlagImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML; - SafeHtml directionHtml = (upperFlagImage != null) ? makeImagePrototype(directionImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML; + SafeHtml directionHtml = (directionImage != null) ? makeImagePrototype(directionImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML; SafeHtmlBuilder builder = new SafeHtmlBuilder(); SafeStyles imgStyle = getImageStyle(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index a48b65e4c5e..23604739ef5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -830,11 +830,16 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (startTime != null) { FlagPoleState activeFlagState = state.getRacingProcedure().getActiveFlags(startTime, MillisecondsTimePoint.now()); List activeFlags = activeFlagState.getCurrentState(); + FlagPoleState previousFlagState = activeFlagState.getPreviousState(state.getRacingProcedure(), startTime); + List previousFlags = previousFlagState.getCurrentState(); + FlagPole mostInterestingFlagPole = FlagPoleState.getMostInterestingFlagPole(previousFlags, activeFlags); + // TODO: adapt the LastFlagFinder#getMostRecent method! - if (!activeFlags.isEmpty()) { - raceInfoDTO.lastUpperFlag = activeFlags.get(0).getUpperFlag(); - raceInfoDTO.lastLowerFlag = activeFlags.get(0).getLowerFlag(); - raceInfoDTO.isLastFlagDisplayed = activeFlags.get(0).isDisplayed(); + if (mostInterestingFlagPole != null) { + raceInfoDTO.lastUpperFlag = mostInterestingFlagPole.getUpperFlag(); + raceInfoDTO.lastLowerFlag = mostInterestingFlagPole.getLowerFlag(); + raceInfoDTO.lastFlagsAreDisplayed = mostInterestingFlagPole.isDisplayed(); + raceInfoDTO.lastFlagsDisplayedStateChanged = previousFlagState.hasPoleChanged(mostInterestingFlagPole); } } @@ -847,7 +852,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (raceInfoDTO.lastStatus.equals(RaceLogRaceStatus.UNSCHEDULED)) { raceInfoDTO.lastUpperFlag = abortingFlagEvent.getUpperFlag(); raceInfoDTO.lastLowerFlag = abortingFlagEvent.getLowerFlag(); - raceInfoDTO.isLastFlagDisplayed = abortingFlagEvent.isDisplayed(); + raceInfoDTO.lastFlagsAreDisplayed = abortingFlagEvent.isDisplayed(); + raceInfoDTO.lastFlagsDisplayedStateChanged = true; } } @@ -864,7 +870,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceInfoDTO.protestFinishTime = protestStartTime.plus(protestDuration).asDate(); raceInfoDTO.lastUpperFlag = Flags.BRAVO; raceInfoDTO.lastLowerFlag = Flags.NONE; - raceInfoDTO.isLastFlagDisplayed = true; + raceInfoDTO.lastFlagsAreDisplayed = true; + raceInfoDTO.lastFlagsDisplayedStateChanged = true; } } @@ -883,7 +890,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceInfoDTO.isTracked = raceColumn.getTrackedRace(fleet) != null ? true : false; return raceInfoDTO; - } + } private void fillStartProcedureSpecifics(RaceInfoDTO raceInfoDTO, ReadonlyRaceState state) { RaceInfoExtensionDTO info = null; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java index 485186a356e..9b436a8bf1a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java @@ -49,7 +49,8 @@ public class RaceInfoDTO implements IsSerializable { public Date lastUpdateTime; public Flags lastUpperFlag; public Flags lastLowerFlag; - public boolean isLastFlagDisplayed; + public boolean lastFlagsAreDisplayed; + public boolean lastFlagsDisplayedStateChanged; public boolean isRaceAbortedInPassBefore; public boolean isTracked; public RaceCourseDTO lastCourseDesign; diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/BaseRaceInfoRaceFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/BaseRaceInfoRaceFragment.java index a1411dca069..9a820c00b76 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/BaseRaceInfoRaceFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/BaseRaceInfoRaceFragment.java @@ -102,7 +102,7 @@ public abstract class BaseRaceInfoRaceFragment flagChanges = flagState.computeUpcomingChanges(); if (!flagChanges.isEmpty()) { TimePoint changeAt = flagState.getNextStateValidFrom(); - FlagPole changePole = getMostInterestingFlagPole(flagChanges); + FlagPole changePole = FlagPoleState.getMostInterestingFlagPole(flagChanges); renderFlagChangesCountdown(targetView, changeAt, changePole); flagPoleCache = new FlagPoleCache(changePole, changeAt); @@ -125,15 +125,6 @@ public abstract class BaseRaceInfoRaceFragment poles) { - for (FlagPole pole : poles) { - if (pole.isDisplayed()) { - return pole; - } - } - return poles.get(0); - } - private void renderFlagChangesCountdown(TextView targetView, TimePoint changeAt, FlagPole changePole) { long millisecondsTillChange = TimeUtils.timeUntil(changeAt); int formatTextResourceId = changePole.isDisplayed() ? R.string.race_startphase_countdown_mode_display : From f65d5584d9b7bdf24aea9c9e7536a29f864f6afe Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 00:16:04 +0200 Subject: [PATCH 089/110] prepared time-keeping for getRaceMapData which may be a performance hog under certain conditions --- .../com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index a48b65e4c5e..8a82c58bf17 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1398,9 +1398,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException { - return new CompactRaceMapDataDTO(getBoatPositions(raceIdentifier, fromPerCompetitorIdAsString, - toPerCompetitorIdAsString, extrapolate), getCoursePositions(raceIdentifier, date), getCourseSidelines(raceIdentifier, date), getQuickRanks( - raceIdentifier, date)); + final Map> boatPositions = getBoatPositions(raceIdentifier, fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate); + final CoursePositionsDTO coursePositions = getCoursePositions(raceIdentifier, date); + final List courseSidelines = getCourseSidelines(raceIdentifier, date); + final List quickRanks = getQuickRanks(raceIdentifier, date); + return new CompactRaceMapDataDTO(boatPositions, coursePositions, courseSidelines, quickRanks); } /** From 7a5e8d5be3c4a90e10941b0e56ec04e10955091d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 14:20:33 +0000 Subject: [PATCH 090/110] Updated amazon ec2 (markdown) --- wiki/amazon-ec2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wiki/amazon-ec2.md b/wiki/amazon-ec2.md index 04bbbaef528..f7bb13f6271 100644 --- a/wiki/amazon-ec2.md +++ b/wiki/amazon-ec2.md @@ -267,7 +267,8 @@ Here are the steps to create a load balanced setup: - Create a master instance holding all data (see http://wiki.sapsailing.com/wiki/amazon-ec2#Setting-up-Master-and-Replica) - Create `n` instances that are configured to connect to the master server -- Create a load balancer that redirects everything from port 80 to let's say port 8888. +- Create a load balancer that redirects everything from HTTP port 8888 to HTTP port 8888 and leave the other switches and checkboxes on their default value +- As "Ping Port" enter HTTP port 8888 and use /index.html as the "Ping Path." - Associate all your instances - Connect your domain with the IP of the load balancer. It could be a good idea to use an Elastic IP that always stays the same for the domain and associate it with your load balancer. That way you can also easily switch between a load balancer and a single instance setup. From bb83fb7eb837e6940e1d0ed915692e393b343e4d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 14:22:04 +0000 Subject: [PATCH 091/110] Notes on ELB configuration --- wiki/amazon-ec2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wiki/amazon-ec2.md b/wiki/amazon-ec2.md index f7bb13f6271..df6372eb584 100644 --- a/wiki/amazon-ec2.md +++ b/wiki/amazon-ec2.md @@ -268,7 +268,8 @@ Here are the steps to create a load balanced setup: - Create a master instance holding all data (see http://wiki.sapsailing.com/wiki/amazon-ec2#Setting-up-Master-and-Replica) - Create `n` instances that are configured to connect to the master server - Create a load balancer that redirects everything from HTTP port 8888 to HTTP port 8888 and leave the other switches and checkboxes on their default value -- As "Ping Port" enter HTTP port 8888 and use /index.html as the "Ping Path." +- As "Ping Port" enter HTTP port 8888 and use /index.html as the "Ping Path." Leave the other values on their defaults again. +- Put the ELB into the "Sailing Analytics App" security group as it will appear in the landscape as a regular sailing analytics application server. - Associate all your instances - Connect your domain with the IP of the load balancer. It could be a good idea to use an Elastic IP that always stays the same for the domain and associate it with your load balancer. That way you can also easily switch between a load balancer and a single instance setup. From a2fa1dabb19c284cd9badac06e87395892d7bbac Mon Sep 17 00:00:00 2001 From: fmittag Date: Wed, 25 Jun 2014 17:09:00 +0200 Subject: [PATCH 092/110] changed call to getRacesTimesInfos() to get executed by a AsyncActionsExecutor --- .../ui/actions/GetRaceTimesInfoAction.java | 25 +++++++++++++++++++ .../gwt/ui/client/RaceTimesInfoProvider.java | 10 +++++--- .../gwt/ui/leaderboard/LeaderboardPanel.java | 2 +- .../gwt/ui/raceboard/RaceBoardEntryPoint.java | 6 +++-- .../gwt/ui/raceboard/RaceBoardPanel.java | 4 +-- .../sailing/gwt/ui/tv/TVViewController.java | 8 +++--- 6 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceTimesInfoAction.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceTimesInfoAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceTimesInfoAction.java new file mode 100644 index 00000000000..bb23803f5f8 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceTimesInfoAction.java @@ -0,0 +1,25 @@ +package com.sap.sailing.gwt.ui.actions; + +import java.util.Collection; +import java.util.List; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; +import com.sap.sse.gwt.client.async.AsyncAction; + +public class GetRaceTimesInfoAction implements AsyncAction> { + private final SailingServiceAsync sailingService; + private final Collection raceIdentifiers; + + public GetRaceTimesInfoAction(SailingServiceAsync sailingService, Collection raceIdentifiers) { + this.sailingService = sailingService; + this.raceIdentifiers = raceIdentifiers; + } + + @Override + public void execute(AsyncCallback> callback) { + sailingService.getRaceTimesInfos(raceIdentifiers, callback); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimesInfoProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimesInfoProvider.java index ea993e51006..f01b507a3b1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimesInfoProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimesInfoProvider.java @@ -17,26 +17,29 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; +import com.sap.sailing.gwt.ui.actions.GetRaceTimesInfoAction; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; +import com.sap.sse.gwt.client.async.AsyncActionsExecutor; public class RaceTimesInfoProvider { private final SailingServiceAsync sailingService; + private final AsyncActionsExecutor asyncActionsExecutor; private final ErrorReporter errorReporter; private final Set raceIdentifiers; private long requestIntervalInMillis; private final HashMap raceTimesInfos; - private final Set listeners; /** * The raceIdentifiers has to be non-null, but can be empty. */ - public RaceTimesInfoProvider(SailingServiceAsync sailingService, ErrorReporter errorReporter, + public RaceTimesInfoProvider(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Collection raceIdentifiers, long requestIntervalInMillis) { this.sailingService = sailingService; + this.asyncActionsExecutor = asyncActionsExecutor; this.errorReporter = errorReporter; this.raceIdentifiers = new HashSet(raceIdentifiers); this.requestIntervalInMillis = requestIntervalInMillis; @@ -97,7 +100,8 @@ public class RaceTimesInfoProvider { private void readTimesInfos() { if (!raceIdentifiers.isEmpty()) { final long clientTimeWhenRequestWasSent = System.currentTimeMillis(); - sailingService.getRaceTimesInfos(raceIdentifiers, new AsyncCallback>() { + GetRaceTimesInfoAction getRaceTimesInfoAction = new GetRaceTimesInfoAction(sailingService, raceIdentifiers); + asyncActionsExecutor.execute(getRaceTimesInfoAction, new AsyncCallback>() { @Override public void onFailure(Throwable caught) { errorReporter.reportError("Error trying to obtain the race time infos: " + caught.getMessage(), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index f5280b01489..ad60b4bbf7d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -518,7 +518,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS } else { trackedRacesIdentifiers = Collections.emptyList(); } - raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), errorReporter, + raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), asyncActionsExecutor, errorReporter, trackedRacesIdentifiers, timer.getRefreshInterval()); } return raceTimesInfoProvider; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 858d0d7a9b8..081e1b41058 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -40,6 +40,7 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sailing.gwt.ui.shared.UserDTO; import com.sap.sse.gwt.client.EntryPointHelper; +import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer.PlayModes; import com.sap.sse.gwt.client.useragent.UserAgentChecker; @@ -170,8 +171,9 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { List singletonList = Collections.singletonList(selectedRace.getRaceIdentifier()); raceSelectionModel.setSelection(singletonList); Timer timer = new Timer(PlayModes.Replay, 1000l); - RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, this, singletonList, 5000l /* requestInterval*/); - RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, user, timer, raceSelectionModel, leaderboardName, + AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor(); + RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, singletonList, 5000l /* requestInterval*/); + RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, asyncActionsExecutor, user, timer, raceSelectionModel, leaderboardName, leaderboardGroupName, raceboardViewConfig, RaceBoardEntryPoint.this, stringMessages, userAgent, raceTimesInfoProvider, showMapControls); raceBoardPanel.fillRegattas(regattas); createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 2b8d3d7e920..d78ef28908c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -131,7 +131,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra private final static String LOCAL_STORAGE_COMPETITORS_FILTER_SETS_KEY = "sailingAnalytics.raceBoard.competitorsFilterSets"; - public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, UserDTO theUser, Timer timer, + public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, AsyncActionsExecutor asyncActionsExecutor, UserDTO theUser, Timer timer, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName, String leaderboardGroupName, RaceBoardViewConfiguration raceboardViewConfiguration, ErrorReporter errorReporter, final StringMessages stringMessages, UserAgentDetails userAgent, RaceTimesInfoProvider raceTimesInfoProvider, boolean showMapControls) { @@ -150,7 +150,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra racesByIdentifier = new HashMap(); selectedRaceIdentifier = raceSelectionProvider.getSelectedRaces().iterator().next(); this.setRaceBoardName(selectedRaceIdentifier.getRaceName()); - asyncActionsExecutor = new AsyncActionsExecutor(); + this.asyncActionsExecutor = asyncActionsExecutor; FlowPanel mainPanel = new FlowPanel(); mainPanel.setSize("100%", "100%"); setWidget(mainPanel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVViewController.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVViewController.java index ae6a1c858b5..241a1fd7a79 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVViewController.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/tv/TVViewController.java @@ -45,6 +45,7 @@ public class TVViewController implements RaceTimesInfoProviderListener { private final UserAgentDetails userAgent; private final LogoAndTitlePanel logoAndTitlePanel; private final DockLayoutPanel dockPanel; + private final AsyncActionsExecutor asyncActionsExecutor; /** * We use two timers: one for the leaderboard, one for the race board. This way, we can stop one while the other continues. @@ -93,6 +94,7 @@ public class TVViewController implements RaceTimesInfoProviderListener { this.dockPanel = dockPanel; this.showRaceDetails = showRaceDetails; + asyncActionsExecutor = new AsyncActionsExecutor(); showWindChart = false; leaderboard = null; leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false); @@ -106,13 +108,13 @@ public class TVViewController implements RaceTimesInfoProviderListener { raceboardTimer.setRefreshInterval(REFRESH_INTERVAL_IN_MILLIS_RACEBOARD); raceboardTimer.play(); - raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, errorReporter, new ArrayList(), 3000l); + raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, errorReporter, new ArrayList(), 3000l); raceTimesInfoProvider.addRaceTimesInfoProviderListener(this); } private LeaderboardPanel createLeaderboardPanel(String leaderboardGroupName, String leaderboardName, boolean showRaceDetails) { CompetitorSelectionModel selectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true); - LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardSettings, + LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderboardSettings, /* preSelectedRace */null, selectionModel, leaderboardTimer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false, /* adjustTimerDelay */ true) { @Override @@ -146,7 +148,7 @@ public class TVViewController implements RaceTimesInfoProviderListener { RaceSelectionModel raceSelectionModel = new RaceSelectionModel(); List singletonList = Collections.singletonList(raceToShow); raceSelectionModel.setSelection(singletonList); - RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, null, raceboardTimer, raceSelectionModel, leaderboardName, + RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, asyncActionsExecutor,null, raceboardTimer, raceSelectionModel, leaderboardName, null, raceboardViewConfig, errorReporter, stringMessages, userAgent, raceTimesInfoProvider, /* showMapControls */ false); return raceBoardPanel; } From 2541567dacea6d271a06384dc875698305681cda Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 17:19:54 +0200 Subject: [PATCH 093/110] comment typo --- .../sailing/domain/leaderboard/caching/LeaderboardDTOCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LeaderboardDTOCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LeaderboardDTOCache.java index 69775b22a9b..50eed2866d7 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LeaderboardDTOCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LeaderboardDTOCache.java @@ -88,7 +88,7 @@ public class LeaderboardDTOCache implements LeaderboardCache { private static final long serialVersionUID = 7287916997229815039L; @Override protected boolean removeEldestEntry(Map.Entry, Boolean>, FutureTask> e) { - return size() > 10; // remember 10 LeaderboardDTOs per leaderborad + return size() > 10; // remember 10 LeaderboardDTOs per leaderboard } }; leaderboardCacheLock = new NamedReentrantReadWriteLock("leaderboardCacheLock for "+leaderboard.getName(), /* fair */ false); From a0186dbfdc08fc6f43ce4049116aa4045f10eb4e Mon Sep 17 00:00:00 2001 From: Lennart Hensler Date: Wed, 25 Jun 2014 18:20:26 +0200 Subject: [PATCH 094/110] Created an optimized version of getTack, if the wind is known --- .../domain/test/mock/MockedTrackedRace.java | 6 +++ ...ockedTrackedRaceWithStartTimeAndRanks.java | 6 +++ .../sailing/domain/tracking/TrackedRace.java | 9 ++++ .../domain/tracking/impl/TrackedRaceImpl.java | 50 +++++++++++++++++-- .../gwt/ui/test/MockedTrackedRace.java | 6 +++ .../gwt/ui/server/SailingServiceImpl.java | 18 ++----- .../server/masterdata/DummyTrackedRace.java | 6 +++ 7 files changed, 84 insertions(+), 17 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index b4e26c5d3e1..2897a93e774 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -538,6 +538,12 @@ public class MockedTrackedRace implements DynamicTrackedRace { // TODO Auto-generated method stub return null; } + + @Override + public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + // TODO Auto-generated method stub + return null; + } @Override public Wind getDirectionFromStartToNextMark(TimePoint at) { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index dd8dd878eb8..a1f722d9607 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -315,6 +315,12 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { // TODO Auto-generated method stub return null; } + + @Override + public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + // TODO Auto-generated method stub + return null; + } @Override public TrackedRegatta getTrackedRegatta() { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index a8c104c1c2e..ee4008f6276 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -381,6 +381,15 @@ public interface TrackedRace extends Serializable { */ Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException; + /** + * Determines whether the competitor is sailing on port or starboard tack at the timePoint + * requested.
+ * This method outperforms {@link #getTack(Competitor, TimePoint)}, but requires the correct wind for the given + * time and competitor position.
+ * This method monitors the competitors' GPS tracks. + */ + Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind); + TrackedRegatta getTrackedRegatta(); /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index ba306188b91..9150adb84f1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -1851,6 +1851,43 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl return result; } + /** + * This is probably best explained by example. If the wind bearing is from port to starboard, the situation looks + * like this: + * + *
+     *                                 ^
+     *                 Wind            | Boat
+     *               ----------->      |
+     *                                 |
+     * 
+     * 
+ * + * In this case, the boat gets the wind from port, so the result has to be {@link Tack#PORT}. The angle between the + * boat's heading (which we can only approximate by the boat's course over ground) and the wind bearing in this case + * is 90 degrees. wind.{@link Bearing#getDifferenceTo(Bearing) getDifferenceTo}(boat) in this case will + * return a bearing representing -90 degrees. + *

+ * + * If the wind is blowing the other way, the angle returned by {@link Bearing#getDifferenceTo(Bearing)} will + * correspond to +90 degrees. In other words, a negative angle means starboard tack, a positive angle represents + * port tack. + *

+ * + * For the unlikely case of 0 degrees difference, {@link Tack#STARBOARD} will result. + * + * @return null in case the boat's bearing cannot be determined for timePoint + */ + @Override + public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + final SpeedWithBearing estimatedSpeed = getTrack(competitor).getEstimatedSpeed(timePoint); + Tack result = null; + if (estimatedSpeed != null) { + result = getTack(wind, estimatedSpeed.getBearing()); + } + return result; + } + /** * Based on the wind direction at timePoint and at position where, compares the * boatBearing to the wind's bearing at that time and place and determined the tack. @@ -1861,15 +1898,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl */ private Tack getTack(Position where, TimePoint timePoint, Bearing boatBearing) throws NoWindException { final Wind wind = getWind(where, timePoint); - Tack result; if (wind == null) { throw new NoWindException("Can't determine wind direction in position " + where + " at " + timePoint + ", therefore cannot determine tack"); } + return getTack(wind, boatBearing); + } + + + /** + * Based on the wind, compares the boatBearing to the wind's bearing at + * that time and place and determined the tack. + */ + private Tack getTack(Wind wind, Bearing boatBearing) { Bearing windBearing = wind.getBearing(); Bearing difference = windBearing.getDifferenceTo(boatBearing); - result = difference.getDegrees() <= 0 ? Tack.PORT : Tack.STARBOARD; - return result; + return difference.getDegrees() <= 0 ? Tack.PORT : Tack.STARBOARD; } @Override diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index 5fe563b47a4..3018a726651 100755 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -539,6 +539,12 @@ public class MockedTrackedRace implements DynamicTrackedRace { // TODO Auto-generated method stub return null; } + + @Override + public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + // TODO Auto-generated method stub + return null; + } @Override public Wind getDirectionFromStartToNextMark(TimePoint at) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 8a82c58bf17..8df19710e9a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1472,17 +1472,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S GPSFixMoving fix = fixIter.next(); while (fix != null && (fix.getTimePoint().compareTo(toTimePointExcluding) < 0 || (fix.getTimePoint().equals(toTimePointExcluding) && toTimePointExcluding.equals(fromTimePoint)))) { - Tack tack; - try { - tack = trackedRace.getTack(competitor, fix.getTimePoint()); - } catch (NoWindException nwe) { - tack = null; - } + Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding); + Tack tack = wind == null? null : trackedRace.getTack(competitor, fix.getTimePoint(), wind); TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, fix.getTimePoint()); LegType legType = trackedLegOfCompetitor == null ? null : trackedRace.getTrackedLeg( trackedLegOfCompetitor.getLeg()).getLegType(fix.getTimePoint()); - Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding); WindDTO windDTO = wind == null ? null : createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding); GPSFixDTO fixDTO = createGPSFixDTO(fix, track.getEstimatedSpeed(fix.getTimePoint()), windDTO, tack, legType, /* extrapolate */ false); @@ -1493,17 +1488,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S // check if fix was at date and if extrapolation is requested if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) { Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate); - Tack tack2; - try { - tack2 = trackedRace.getTack(competitor, toTimePointExcluding); - } catch (NoWindException nwe) { - tack2 = null; - } + Wind wind2 = trackedRace.getWind(position, toTimePointExcluding); + Tack tack2 = wind2 == null ? null : trackedRace.getTack(competitor, toTimePointExcluding, wind2); LegType legType2 = trackedLegOfCompetitor == null ? null : trackedRace .getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType( fix.getTimePoint()); SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding); - Wind wind2 = trackedRace.getWind(position, toTimePointExcluding); WindDTO windDTO2 = wind2 == null ? null : createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding); GPSFixDTO extrapolated = new GPSFixDTO( toPerCompetitorIdAsString.get(competitorDTO.getIdAsString()), diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index 87d354c2a75..8137f2e4f7a 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -277,6 +277,12 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { // TODO Auto-generated method stub return null; } + + @Override + public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + // TODO Auto-generated method stub + return null; + } @Override public TrackedRegatta getTrackedRegatta() { From 3863b9b8a68cad6d62a90eeee784bc3cfc3511bb Mon Sep 17 00:00:00 2001 From: Armin Zamani Date: Wed, 25 Jun 2014 18:47:43 +0200 Subject: [PATCH 095/110] Changed version code in Android manifest file --- mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml b/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml index 1bbc1362f11..5b3675a59e8 100644 --- a/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml +++ b/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="6" + android:versionName="6 - Laser" > Date: Wed, 25 Jun 2014 19:24:42 +0200 Subject: [PATCH 096/110] Added caching of LegTypes for getBoatPositions The caching happens locally in the method with a resolution of one minute. This should increase the performance of the method. --- .../gwt/ui/server/SailingServiceImpl.java | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 8df19710e9a..45944ea95b7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1398,7 +1398,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException { - final Map> boatPositions = getBoatPositions(raceIdentifier, fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate); + final Map> boatPositions = getBoatPositions(raceIdentifier, + fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate); final CoursePositionsDTO coursePositions = getCoursePositions(raceIdentifier, date); final List courseSidelines = getCourseSidelines(raceIdentifier, date); final List quickRanks = getQuickRanks(raceIdentifier, date); @@ -1406,6 +1407,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } /** + * {@link LegType}s are cached within the method with a resolution of one minute. The cache key is a pair of + * {@link TrackedLegOfCompetitor} and {@link TimePoint}. + * * @param from * for the list of competitors provided as keys of this map, requests the GPS fixes starting with the * date provided as value @@ -1422,7 +1426,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S */ private Map> getBoatPositions(RegattaAndRaceIdentifier raceIdentifier, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, - boolean extrapolate) throws NoWindException { + boolean extrapolate) + throws NoWindException { + Map, LegType> legTypeCache = new HashMap<>(); Map> result = new HashMap>(); TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { @@ -1476,8 +1482,21 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Tack tack = wind == null? null : trackedRace.getTack(competitor, fix.getTimePoint(), wind); TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, fix.getTimePoint()); - LegType legType = trackedLegOfCompetitor == null ? null : trackedRace.getTrackedLeg( - trackedLegOfCompetitor.getLeg()).getLegType(fix.getTimePoint()); + LegType legType; + if (trackedLegOfCompetitor != null) { + TimePoint quantifiedTimePoint = quantifyTimePointWithResolution(fix.getTimePoint(), /* resolutionInMilliseconds */ + 60000); + Pair cacheKey = new Pair( + trackedLegOfCompetitor, quantifiedTimePoint); + legType = legTypeCache.get(cacheKey); + if (legType == null) { + legType = trackedRace.getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType( + fix.getTimePoint()); + legTypeCache.put(cacheKey, legType); + } + } else { + legType = null; + } WindDTO windDTO = wind == null ? null : createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding); GPSFixDTO fixDTO = createGPSFixDTO(fix, track.getEstimatedSpeed(fix.getTimePoint()), windDTO, tack, legType, /* extrapolate */ false); @@ -1490,9 +1509,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate); Wind wind2 = trackedRace.getWind(position, toTimePointExcluding); Tack tack2 = wind2 == null ? null : trackedRace.getTack(competitor, toTimePointExcluding, wind2); - LegType legType2 = trackedLegOfCompetitor == null ? null : trackedRace - .getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType( + LegType legType2; + if (trackedLegOfCompetitor != null) { + TimePoint quantifiedTimePoint = quantifyTimePointWithResolution( + fix.getTimePoint(), /* resolutionInMilliseconds */ + 60000); + Pair cacheKey = new Pair( + trackedLegOfCompetitor, quantifiedTimePoint); + legType2 = legTypeCache.get(cacheKey); + if (legType2 == null) { + legType2 = trackedRace.getTrackedLeg(trackedLegOfCompetitor.getLeg()) + .getLegType( fix.getTimePoint()); + legTypeCache.put(cacheKey, legType2); + } + } else { + legType2 = null; + } SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding); WindDTO windDTO2 = wind2 == null ? null : createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding); GPSFixDTO extrapolated = new GPSFixDTO( @@ -1512,6 +1545,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return result; } + private TimePoint quantifyTimePointWithResolution(TimePoint timePoint, long resolutionInMilliseconds) { + return new MillisecondsTimePoint(timePoint.asMillis() / resolutionInMilliseconds * resolutionInMilliseconds); + } + private SpeedWithBearingDTO createSpeedWithBearingDTO(SpeedWithBearing speedWithBearing) { return new SpeedWithBearingDTO(speedWithBearing.getKnots(), speedWithBearing .getBearing().getDegrees()); From e20f0891c266be4488f58e97893349bd331be0e4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 20:18:08 +0200 Subject: [PATCH 097/110] attempting to move TrackedRaceImpl competitor ranks cache to class level and limiting in size; probably too complicated, yet worth a commit on this branch --- .../domain/tracking/impl/TrackedRaceImpl.java | 87 +++++++++++++++---- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index ba306188b91..64707aa49ba 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -17,6 +17,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.NavigableSet; import java.util.Set; import java.util.Timer; @@ -109,6 +110,7 @@ import com.sap.sailing.util.impl.ArrayListNavigableSet; import com.sap.sailing.util.impl.LockUtil; import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Pair; public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials implements CourseListener { private static final long serialVersionUID = -4825546964220003507L; @@ -178,15 +180,32 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl private long updateCount; - private transient Map> competitorRankings; + /** + * To enable cleaning of cache entries in {@link #competitorRankings} for an entire race, this map remembers all time + * points for which cache entries for this race were put into {@link #competitorRankings}. Whenever entries are + * about to be expunged from {@link #competitorRankings}, their time points are removed from this set which is a + * synchronized set. + */ + private transient Set timePointsForWhichCompetitorRankingsCacheEntriesExist; + + /** + * Holds a size-limited cache of the {@link #getCompetitorsFromBestToWorst(TimePoint)} results. When entries are about + * to be expunged, the corresponding key is removed as well from {@link #competitorRankingsLocks} and from + * {@link #timePointsForWhichCompetitorRankingsCacheEntriesExist}.

+ * + * All access to this structure must use synchronized as if this were a synchronized hash map. + */ + private final static LinkedHashMap, List> competitorRankings; /** - * The locks managed here correspond with the {@link #competitorRankings} structure. When + * The locks managed here mainly correspond with the {@link #competitorRankings} structure ("mainly" because when an + * entry is about to be expunged from {@link #competitorRankings} then the corresponding key will first be removed + * from this map, limiting its size in the same way that the size of {@link #competitorRankings} is limited). When * {@link #getCompetitorsFromBestToWorst(TimePoint)} starts to compute rankings, it locks the write lock for the - * time point. Readers use the read lock. Checking / entering a lock into this map uses synchronized on - * the map itself. + * time point / tracked race. Readers use the read lock. Checking / entering a lock into this map uses + * synchronized on the map itself. */ - private transient Map competitorRankingsLocks; + private final static ConcurrentHashMap, NamedReentrantReadWriteLock> competitorRankingsLocks; /** * legs appear in the order in which they appear in the race's course @@ -265,12 +284,33 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl private final NamedReentrantReadWriteLock loadingFromGPSFixStoreLock; private final Map, NamedReentrantReadWriteLock> locksForMarkPassings; + + private static final int MAX_NUMBER_OF_COMPETITOR_RANK_CACHE_ENTRIES = 10000; + + static { + competitorRankings = new LinkedHashMap, List>( + MAX_NUMBER_OF_COMPETITOR_RANK_CACHE_ENTRIES, 0.75f) { + private static final long serialVersionUID = 2826008515372742341L; + + @Override + protected boolean removeEldestEntry(Entry, List> eldest) { + final boolean remove = size() > MAX_NUMBER_OF_COMPETITOR_RANK_CACHE_ENTRIES; + if (remove) { + competitorRankingsLocks.remove(eldest.getKey()); + eldest.getKey().getB().timePointsForWhichCompetitorRankingsCacheEntriesExist.remove(eldest.getKey().getA()); + } + return remove; + } + }; + competitorRankingsLocks = new ConcurrentHashMap<>(); + } public TrackedRaceImpl(final TrackedRegatta trackedRegatta, RaceDefinition race, final Iterable sidelines, final WindStore windStore, final GPSFixStore gpsFixStore, long delayToLiveInMillis, final long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation) { super(race, trackedRegatta, windStore, millisecondsOverWhichToAverageWind); locksForMarkPassings = new IdentityHashMap<>(); + timePointsForWhichCompetitorRankingsCacheEntriesExist = Collections.synchronizedSet(new HashSet()); attachedRaceLogs = new HashMap<>(); this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0); this.statusNotifier = new Object[0]; @@ -374,8 +414,6 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl WindSource trackBasedWindSource = new WindSourceImpl(WindSourceType.TRACK_BASED_ESTIMATION); windTracks.put(trackBasedWindSource, getOrCreateWindTrack(trackBasedWindSource, delayForWindEstimationCacheInvalidation)); - competitorRankings = new HashMap>(); - competitorRankingsLocks = new HashMap(); // now wait until wind loading has at least started; then we know that the serialization lock is safely held by the loader try { waitUntilLoadingFromWindStoreComplete(); @@ -422,12 +460,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); + timePointsForWhichCompetitorRankingsCacheEntriesExist = Collections.synchronizedSet(new HashSet()); attachedRaceLogs = new HashMap<>(); markPassingsTimes = new ArrayList>>(); cacheInvalidationTimerLock = new Object(); windStore = EmptyWindStore.INSTANCE; - competitorRankings = new HashMap>(); - competitorRankingsLocks = new HashMap(); directionFromStartToNextMarkCache = new HashMap>(); crossTrackErrorCache = new CrossTrackErrorCache(this); maneuverCache = createManeuverCache(); @@ -986,18 +1023,19 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl @Override public List getCompetitorsFromBestToWorst(TimePoint timePoint) throws NoWindException { NamedReentrantReadWriteLock readWriteLock; + Pair cacheKey = new Pair(timePoint, this); synchronized (competitorRankingsLocks) { readWriteLock = competitorRankingsLocks.get(timePoint); if (readWriteLock == null) { readWriteLock = new NamedReentrantReadWriteLock("competitor rankings for race " + getRace().getName() + " for time point " + timePoint, /* fair */false); - competitorRankingsLocks.put(timePoint, readWriteLock); + competitorRankingsLocks.put(cacheKey, readWriteLock); } } List rankedCompetitors; final boolean lockForWrite; synchronized (competitorRankings) { - rankedCompetitors = competitorRankings.get(timePoint); + rankedCompetitors = competitorRankings.get(cacheKey); if (rankedCompetitors == null) { lockForWrite = true; } else { @@ -1011,8 +1049,9 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } try { if (rankedCompetitors == null) { - rankedCompetitors = competitorRankings.get(timePoint); // try again; maybe a writer released the write - // lock after updating the cache + synchronized (competitorRankings) { + rankedCompetitors = competitorRankings.get(cacheKey); // try again; maybe a writer released the write + } // lock after updating the cache if (rankedCompetitors == null) { RaceRankComparator comparator = new RaceRankComparator(this, timePoint); rankedCompetitors = new ArrayList(); @@ -1021,7 +1060,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } Collections.sort(rankedCompetitors, comparator); synchronized (competitorRankings) { - competitorRankings.put(timePoint, rankedCompetitors); + competitorRankings.put(cacheKey, rankedCompetitors); } } } @@ -1423,11 +1462,23 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl cacheInvalidationTimer.cancel(); cacheInvalidationTimer = null; } - synchronized (competitorRankings) { - competitorRankings.clear(); + // Important: don't synchronize on timePointsForWhichCompetitorRankingsCacheEntriesExist first and then on + // competitorRankings because the opposite locking order is likely taking place in removeEldestEntry which could + // lead to a deadlock. + Set timePointsToRemoveCacheEntriesFor; + synchronized (timePointsForWhichCompetitorRankingsCacheEntriesExist) { + timePointsToRemoveCacheEntriesFor = new HashSet<>(timePointsForWhichCompetitorRankingsCacheEntriesExist); + timePointsForWhichCompetitorRankingsCacheEntriesExist.clear(); } - synchronized (competitorRankingsLocks) { - competitorRankingsLocks.clear(); + for (TimePoint timePoint : timePointsToRemoveCacheEntriesFor) { + Pair cacheKey = new Pair<>(timePoint, TrackedRaceImpl.this); + synchronized (competitorRankings) { + competitorRankings.remove(cacheKey); + } + synchronized (competitorRankingsLocks) { + competitorRankingsLocks.remove(cacheKey); + } + } } }, DELAY_FOR_CACHE_CLEARING_IN_MILLISECONDS); From 899b854b50d8ce6f2f07ccf28b9fd41a1d806899 Mon Sep 17 00:00:00 2001 From: Lennart Hensler Date: Wed, 25 Jun 2014 21:35:35 +0200 Subject: [PATCH 098/110] Changed the leg type cache key from TrackedLegOfCompetitor and TimePoint to Leg and TimePoint --- .../sailing/gwt/ui/server/SailingServiceImpl.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 45944ea95b7..e26df5ba012 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -67,6 +67,7 @@ import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.LeaderboardGroupBase; import com.sap.sailing.domain.base.LeaderboardSearchResult; import com.sap.sailing.domain.base.LeaderboardSearchResultBase; +import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.RaceColumn; @@ -1428,7 +1429,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException { - Map, LegType> legTypeCache = new HashMap<>(); + Map, LegType> legTypeCache = new HashMap<>(); Map> result = new HashMap>(); TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { @@ -1483,11 +1484,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, fix.getTimePoint()); LegType legType; - if (trackedLegOfCompetitor != null) { + if (trackedLegOfCompetitor != null && trackedLegOfCompetitor.getLeg() != null) { TimePoint quantifiedTimePoint = quantifyTimePointWithResolution(fix.getTimePoint(), /* resolutionInMilliseconds */ 60000); - Pair cacheKey = new Pair( - trackedLegOfCompetitor, quantifiedTimePoint); + Pair cacheKey = new Pair( + trackedLegOfCompetitor.getLeg(), quantifiedTimePoint); legType = legTypeCache.get(cacheKey); if (legType == null) { legType = trackedRace.getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType( @@ -1510,12 +1511,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Wind wind2 = trackedRace.getWind(position, toTimePointExcluding); Tack tack2 = wind2 == null ? null : trackedRace.getTack(competitor, toTimePointExcluding, wind2); LegType legType2; - if (trackedLegOfCompetitor != null) { + if (trackedLegOfCompetitor != null && trackedLegOfCompetitor.getLeg() != null) { TimePoint quantifiedTimePoint = quantifyTimePointWithResolution( fix.getTimePoint(), /* resolutionInMilliseconds */ 60000); - Pair cacheKey = new Pair( - trackedLegOfCompetitor, quantifiedTimePoint); + Pair cacheKey = new Pair( + trackedLegOfCompetitor.getLeg(), quantifiedTimePoint); legType2 = legTypeCache.get(cacheKey); if (legType2 == null) { legType2 = trackedRace.getTrackedLeg(trackedLegOfCompetitor.getLeg()) From b8deee8122249ac1b28a14afcd4a5f00050e5bd4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 22:07:44 +0200 Subject: [PATCH 099/110] limit the competitorRankings cache size per race to 10 entries; fixing bug 2018 --- .../domain/tracking/impl/TrackedRaceImpl.java | 144 +++++++----------- 1 file changed, 54 insertions(+), 90 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 64707aa49ba..b25d32f7fa7 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -110,7 +110,6 @@ import com.sap.sailing.util.impl.ArrayListNavigableSet; import com.sap.sailing.util.impl.LockUtil; import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; import com.sap.sse.common.Util; -import com.sap.sse.common.Util.Pair; public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials implements CourseListener { private static final long serialVersionUID = -4825546964220003507L; @@ -181,31 +180,19 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl private long updateCount; /** - * To enable cleaning of cache entries in {@link #competitorRankings} for an entire race, this map remembers all time - * points for which cache entries for this race were put into {@link #competitorRankings}. Whenever entries are - * about to be expunged from {@link #competitorRankings}, their time points are removed from this set which is a - * synchronized set. + * Limit for the cache size in {@link #competitorRankings} and respectively in {@link #competitorRankingsLocks}. */ - private transient Set timePointsForWhichCompetitorRankingsCacheEntriesExist; + private static final int MAX_COMPETITOR_RANKINGS_CACHE_SIZE = 10; - /** - * Holds a size-limited cache of the {@link #getCompetitorsFromBestToWorst(TimePoint)} results. When entries are about - * to be expunged, the corresponding key is removed as well from {@link #competitorRankingsLocks} and from - * {@link #timePointsForWhichCompetitorRankingsCacheEntriesExist}.

- * - * All access to this structure must use synchronized as if this were a synchronized hash map. - */ - private final static LinkedHashMap, List> competitorRankings; + private transient LinkedHashMap> competitorRankings; /** - * The locks managed here mainly correspond with the {@link #competitorRankings} structure ("mainly" because when an - * entry is about to be expunged from {@link #competitorRankings} then the corresponding key will first be removed - * from this map, limiting its size in the same way that the size of {@link #competitorRankings} is limited). When + * The locks managed here correspond with the {@link #competitorRankings} structure. When * {@link #getCompetitorsFromBestToWorst(TimePoint)} starts to compute rankings, it locks the write lock for the - * time point / tracked race. Readers use the read lock. Checking / entering a lock into this map uses - * synchronized on the map itself. + * time point. Readers use the read lock. Checking / entering a lock into this map uses synchronized on + * the map itself. */ - private final static ConcurrentHashMap, NamedReentrantReadWriteLock> competitorRankingsLocks; + private transient LinkedHashMap competitorRankingsLocks; /** * legs appear in the order in which they appear in the race's course @@ -284,33 +271,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl private final NamedReentrantReadWriteLock loadingFromGPSFixStoreLock; private final Map, NamedReentrantReadWriteLock> locksForMarkPassings; - - private static final int MAX_NUMBER_OF_COMPETITOR_RANK_CACHE_ENTRIES = 10000; - - static { - competitorRankings = new LinkedHashMap, List>( - MAX_NUMBER_OF_COMPETITOR_RANK_CACHE_ENTRIES, 0.75f) { - private static final long serialVersionUID = 2826008515372742341L; - - @Override - protected boolean removeEldestEntry(Entry, List> eldest) { - final boolean remove = size() > MAX_NUMBER_OF_COMPETITOR_RANK_CACHE_ENTRIES; - if (remove) { - competitorRankingsLocks.remove(eldest.getKey()); - eldest.getKey().getB().timePointsForWhichCompetitorRankingsCacheEntriesExist.remove(eldest.getKey().getA()); - } - return remove; - } - }; - competitorRankingsLocks = new ConcurrentHashMap<>(); - } public TrackedRaceImpl(final TrackedRegatta trackedRegatta, RaceDefinition race, final Iterable sidelines, final WindStore windStore, final GPSFixStore gpsFixStore, long delayToLiveInMillis, final long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation) { super(race, trackedRegatta, windStore, millisecondsOverWhichToAverageWind); locksForMarkPassings = new IdentityHashMap<>(); - timePointsForWhichCompetitorRankingsCacheEntriesExist = Collections.synchronizedSet(new HashSet()); attachedRaceLogs = new HashMap<>(); this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0); this.statusNotifier = new Object[0]; @@ -414,6 +380,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl WindSource trackBasedWindSource = new WindSourceImpl(WindSourceType.TRACK_BASED_ESTIMATION); windTracks.put(trackBasedWindSource, getOrCreateWindTrack(trackBasedWindSource, delayForWindEstimationCacheInvalidation)); + competitorRankings = createCompetitorRankingsCache(); + competitorRankingsLocks = createCompetitorRankingsLockMap(); // now wait until wind loading has at least started; then we know that the serialization lock is safely held by the loader try { waitUntilLoadingFromWindStoreComplete(); @@ -422,6 +390,26 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } } + private LinkedHashMap createCompetitorRankingsLockMap() { + return new LinkedHashMap() { + private static final long serialVersionUID = 6298801656693955386L; + @Override + protected boolean removeEldestEntry(Entry eldest) { + return size() > MAX_COMPETITOR_RANKINGS_CACHE_SIZE; + } + }; + } + + private LinkedHashMap> createCompetitorRankingsCache() { + return new LinkedHashMap>() { + private static final long serialVersionUID = -6044369612727021861L; + @Override + protected boolean removeEldestEntry(Entry> eldest) { + return size() > MAX_COMPETITOR_RANKINGS_CACHE_SIZE; + } + }; + } + /** * Assuming that the tracks were loaded from the persistent store, this method updates * the time stamps that frame the data held by this tracked race. See {@link #timePointOfLastEvent}, {@link #timePointOfNewestEvent} @@ -460,11 +448,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); - timePointsForWhichCompetitorRankingsCacheEntriesExist = Collections.synchronizedSet(new HashSet()); attachedRaceLogs = new HashMap<>(); markPassingsTimes = new ArrayList>>(); cacheInvalidationTimerLock = new Object(); windStore = EmptyWindStore.INSTANCE; + competitorRankings = createCompetitorRankingsCache(); + competitorRankingsLocks = createCompetitorRankingsLockMap(); directionFromStartToNextMarkCache = new HashMap>(); crossTrackErrorCache = new CrossTrackErrorCache(this); maneuverCache = createManeuverCache(); @@ -1023,55 +1012,42 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl @Override public List getCompetitorsFromBestToWorst(TimePoint timePoint) throws NoWindException { NamedReentrantReadWriteLock readWriteLock; - Pair cacheKey = new Pair(timePoint, this); synchronized (competitorRankingsLocks) { readWriteLock = competitorRankingsLocks.get(timePoint); if (readWriteLock == null) { readWriteLock = new NamedReentrantReadWriteLock("competitor rankings for race " + getRace().getName() + " for time point " + timePoint, /* fair */false); - competitorRankingsLocks.put(cacheKey, readWriteLock); + competitorRankingsLocks.put(timePoint, readWriteLock); } } List rankedCompetitors; - final boolean lockForWrite; synchronized (competitorRankings) { - rankedCompetitors = competitorRankings.get(cacheKey); - if (rankedCompetitors == null) { - lockForWrite = true; - } else { - lockForWrite = false; - } + rankedCompetitors = competitorRankings.get(timePoint); } - if (lockForWrite) { + if (rankedCompetitors == null) { LockUtil.lockForWrite(readWriteLock); - } else { - LockUtil.lockForRead(readWriteLock); - } - try { - if (rankedCompetitors == null) { - synchronized (competitorRankings) { - rankedCompetitors = competitorRankings.get(cacheKey); // try again; maybe a writer released the write - } // lock after updating the cache + try { if (rankedCompetitors == null) { - RaceRankComparator comparator = new RaceRankComparator(this, timePoint); - rankedCompetitors = new ArrayList(); - for (Competitor c : getRace().getCompetitors()) { - rankedCompetitors.add(c); - } - Collections.sort(rankedCompetitors, comparator); - synchronized (competitorRankings) { - competitorRankings.put(cacheKey, rankedCompetitors); + rankedCompetitors = competitorRankings.get(timePoint); // try again; maybe a writer released the + // write + // lock after updating the cache + if (rankedCompetitors == null) { + RaceRankComparator comparator = new RaceRankComparator(this, timePoint); + rankedCompetitors = new ArrayList(); + for (Competitor c : getRace().getCompetitors()) { + rankedCompetitors.add(c); + } + Collections.sort(rankedCompetitors, comparator); + synchronized (competitorRankings) { + competitorRankings.put(timePoint, rankedCompetitors); + } } } - } - return rankedCompetitors; - } finally { - if (lockForWrite) { + } finally { LockUtil.unlockAfterWrite(readWriteLock); - } else { - LockUtil.unlockAfterRead(readWriteLock); } } + return rankedCompetitors; } @Override @@ -1462,23 +1438,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl cacheInvalidationTimer.cancel(); cacheInvalidationTimer = null; } - // Important: don't synchronize on timePointsForWhichCompetitorRankingsCacheEntriesExist first and then on - // competitorRankings because the opposite locking order is likely taking place in removeEldestEntry which could - // lead to a deadlock. - Set timePointsToRemoveCacheEntriesFor; - synchronized (timePointsForWhichCompetitorRankingsCacheEntriesExist) { - timePointsToRemoveCacheEntriesFor = new HashSet<>(timePointsForWhichCompetitorRankingsCacheEntriesExist); - timePointsForWhichCompetitorRankingsCacheEntriesExist.clear(); + synchronized (competitorRankings) { + competitorRankings.clear(); } - for (TimePoint timePoint : timePointsToRemoveCacheEntriesFor) { - Pair cacheKey = new Pair<>(timePoint, TrackedRaceImpl.this); - synchronized (competitorRankings) { - competitorRankings.remove(cacheKey); - } - synchronized (competitorRankingsLocks) { - competitorRankingsLocks.remove(cacheKey); - } - + synchronized (competitorRankingsLocks) { + competitorRankingsLocks.clear(); } } }, DELAY_FOR_CACHE_CLEARING_IN_MILLISECONDS); From 148f7936f2d2075eaffa9d8624b29bd30b8b5ef1 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 23:10:45 +0200 Subject: [PATCH 100/110] log size of inbound replication queue (number of received by yet unprocessed messages) --- .../server/replication/impl/Replicator.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java b/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java index 3d86f9f5f0c..0784e12911e 100755 --- a/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java +++ b/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java @@ -3,9 +3,11 @@ package com.sap.sailing.server.replication.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.BlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; @@ -83,12 +85,31 @@ public class Replicator implements Runnable { @Override public void run() { ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + long messageCount = 0; + Field _queue; + try { + _queue = QueueingConsumer.class.getDeclaredField("_queue"); + _queue.setAccessible(true); + } catch (Exception e) { + _queue = null; + } while (true) { if (isBeingStopped()) { break; } try { Delivery delivery = consumer.nextDelivery(); + messageCount++; + if (_queue != null) { + if (messageCount % 10000l == 0) { + try { + logger.info("Inbound replication queue size: "+((BlockingQueue) _queue.get(consumer)).size()); + } catch (Exception e) { + // it didn't work; but it's a log message only... + logger.info("Received another 10000 replication messages"); + } + } + } byte[] bytesFromMessage = delivery.getBody(); checksPerformed = 0; // Set this object's class's class loader as context for de-serialization so that all exported classes From d156cfa7e9f4ae1161c1bebea783716f1e1225aa Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 23:22:10 +0200 Subject: [PATCH 101/110] and here the aftermath to c2036cae1de457b65afba3a326309502b5e985c1 which was still missing an initial check for master's end of tracking time to be set --- .../server/replication/test/TrackRaceReplicationTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index 8318caee327..00a586a9f6b 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -152,10 +152,11 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { public void testRaceTimeReplication() throws InterruptedException, Exception { startTracking(); // now wait at least until the start of tracking time has been received on the master copy - boolean receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null; - while (!receivedStartOfTracking) { // relying on the Timeout rule for this test + boolean receivedStartAndEndOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null && + master.getTrackedRace(raceIdentifier).getEndOfTracking() != null; + while (!receivedStartAndEndOfTracking) { // relying on the Timeout rule for this test Thread.sleep(10); - receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null && + receivedStartAndEndOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null && master.getTrackedRace(raceIdentifier).getEndOfTracking() != null; } Thread.sleep(2000); // kept failing several times for a 1000ms timeout From 216c2e5cdd991fe2d4250e51ffa0b9d1686001f4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 23:28:37 +0200 Subject: [PATCH 102/110] support concurrent replication operation execution for wind fixes and competitor and mark fixes; fixing bug 2020 --- .../server/replication/impl/Replicator.java | 34 +++++++++++++++++-- .../AbstractWindOperation.java | 9 +++++ .../RecordCompetitorGPSFix.java | 9 +++++ .../RecordMarkGPSFix.java | 9 +++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java b/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java index 0784e12911e..d5db44e611c 100755 --- a/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java +++ b/java/com.sap.sailing.server.replication/src/com/sap/sailing/server/replication/impl/Replicator.java @@ -8,6 +8,10 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -57,6 +61,22 @@ public class Replicator implements Runnable { private boolean stopped = false; + /** + * When many updates are triggered in a short period of time by a single thread, ensure that the single thread + * providing the updates is not outperformed by all the re-calculations happening here. Leave at least one + * core to other things, but by using at least three threads ensure that no simplistic deadlocks may occur. + */ + private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()-1, 3); + + /** + * Used for the parallel execution of operations that don't + * {@link RacingEventServiceOperation#requiresSynchronousExecution()}. + */ + private final static Executor executor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE, + /* maximumPoolSize */ THREAD_POOL_SIZE, + /* keepAliveTime */ 60, TimeUnit.SECONDS, + /* workQueue */ new LinkedBlockingQueue()); + /** * @param master * descriptor of the master server from which this replicator receives messages @@ -193,8 +213,18 @@ public class Replicator implements Runnable { } } - private synchronized void apply(RacingEventServiceOperation operation) { - racingEventServiceTracker.getRacingEventService().apply(operation); + private synchronized void apply(final RacingEventServiceOperation operation) { + Runnable runnable = new Runnable() { + @Override + public void run() { + racingEventServiceTracker.getRacingEventService().apply(operation); + } + }; + if (operation.requiresSynchronousExecution()) { + runnable.run(); + } else { + executor.execute(runnable); + } } private synchronized void queue(RacingEventServiceOperation operation) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractWindOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractWindOperation.java index f06eb3869f7..12b496fbe98 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractWindOperation.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractWindOperation.java @@ -15,6 +15,15 @@ public abstract class AbstractWindOperation extends AbstractRaceOperation this.wind = wind; } + /** + * Operations of this type can be run in parallel to other operations; subsequent operations do not have to wait + * for this operation's completion. + */ + @Override + public boolean requiresSynchronousExecution() { + return false; + } + protected WindSource getWindSource() { return windSource; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordCompetitorGPSFix.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordCompetitorGPSFix.java index 16859ed67ba..9b674d723d6 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordCompetitorGPSFix.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordCompetitorGPSFix.java @@ -18,6 +18,15 @@ public class RecordCompetitorGPSFix extends AbstractRaceOperation { this.gpsFix = gpsFix; } + /** + * Operations of this type can be run in parallel to other operations; subsequent operations do not have to wait + * for this operation's completion. + */ + @Override + public boolean requiresSynchronousExecution() { + return false; + } + @Override public Void internalApplyTo(RacingEventService toState) throws Exception { DynamicTrackedRace trackedRace = (DynamicTrackedRace) toState.getTrackedRace(getRaceIdentifier()); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordMarkGPSFix.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordMarkGPSFix.java index 9a65ca51599..9bf8d9d790e 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordMarkGPSFix.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RecordMarkGPSFix.java @@ -18,6 +18,15 @@ public class RecordMarkGPSFix extends AbstractRaceOperation { this.fix = fix; } + /** + * Operations of this type can be run in parallel to other operations; subsequent operations do not have to wait + * for this operation's completion. + */ + @Override + public boolean requiresSynchronousExecution() { + return false; + } + @Override public Void internalApplyTo(RacingEventService toState) throws Exception { DynamicTrackedRace trackedRace = (DynamicTrackedRace) toState.getTrackedRace(getRaceIdentifier()); From fb3b794b336edd7faf4e48dcdd8b6ade46e62778 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 25 Jun 2014 23:34:30 +0200 Subject: [PATCH 103/110] fixed wind add/remove replication test now that the wind replication operations may run asynchronously --- .../replication/test/TrackedRaceContentsReplicationTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java index 377b664b4b3..8f24b9d7b7a 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java @@ -164,6 +164,7 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio new KnotSpeedWithBearingImpl(13, new DegreeBearingImpl(234))); WindSource webWindSource = new WindSourceImpl(WindSourceType.WEB); trackedRace.recordWind(wind, webWindSource); + Thread.sleep(500); // wind addition and wind removal are asynchronous operations that could otherwise pass each other trackedRace.removeWind(wind, webWindSource); final WindTrack windTrack = trackedRace.getOrCreateWindTrack(webWindSource); windTrack.lockForRead(); From 1f9d3f73061ca3dfc48e1c5fcf42c757685c5372 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 26 Jun 2014 01:33:03 +0200 Subject: [PATCH 104/110] re-use getEstimatedSpeed calculation in SailingServiceImpl.getBoatPositions --- .../domain/test/mock/MockedTrackedRace.java | 3 ++- .../MockedTrackedRaceWithStartTimeAndRanks.java | 3 ++- .../sap/sailing/domain/tracking/TrackedRace.java | 13 ++++++++----- .../domain/tracking/impl/TrackedRaceImpl.java | 3 +-- .../sailing/gwt/ui/test/MockedTrackedRace.java | 3 ++- .../sailing/gwt/ui/server/SailingServiceImpl.java | 13 +++++++------ .../SailingServer (No Proxy).launch | 15 ++++++++------- .../server/masterdata/DummyTrackedRace.java | 3 ++- 8 files changed, 32 insertions(+), 24 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index 2897a93e774..7c3b100e21d 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -28,6 +28,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WindSource; @@ -540,7 +541,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) { // TODO Auto-generated method stub return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index a1f722d9607..86f2affe269 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -20,6 +20,7 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WindSource; @@ -317,7 +318,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { } @Override - public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) { // TODO Auto-generated method stub return null; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index ee4008f6276..21899cb2620 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -20,6 +20,7 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.TimingConstants; @@ -383,12 +384,14 @@ public interface TrackedRace extends Serializable { /** * Determines whether the competitor is sailing on port or starboard tack at the timePoint - * requested.
- * This method outperforms {@link #getTack(Competitor, TimePoint)}, but requires the correct wind for the given - * time and competitor position.
- * This method monitors the competitors' GPS tracks. + * requested. + *

+ * This method outperforms {@link #getTack(Competitor, TimePoint)}, based on being passed an already calculated wind + * for the given time and competitor position as well as the competitors speed and course over ground. + *

+ * This method will acquire the read lock for the competitor's track. */ - Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind); + Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint); TrackedRegatta getTrackedRegatta(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 52e6bc77280..caea19a3180 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -1894,8 +1894,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl * @return null in case the boat's bearing cannot be determined for timePoint */ @Override - public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { - final SpeedWithBearing estimatedSpeed = getTrack(competitor).getEstimatedSpeed(timePoint); + public Tack getTack(SpeedWithBearing estimatedSpeed, Wind wind, TimePoint timePoint) { Tack result = null; if (estimatedSpeed != null) { result = getTack(wind, estimatedSpeed.getBearing()); diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index 3018a726651..5689561de52 100755 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -27,6 +27,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WindSource; @@ -541,7 +542,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) { // TODO Auto-generated method stub return null; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index b0b74ecd0b7..149dd8cd9c5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1486,8 +1486,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S GPSFixMoving fix = fixIter.next(); while (fix != null && (fix.getTimePoint().compareTo(toTimePointExcluding) < 0 || (fix.getTimePoint().equals(toTimePointExcluding) && toTimePointExcluding.equals(fromTimePoint)))) { - Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding); - Tack tack = wind == null? null : trackedRace.getTack(competitor, fix.getTimePoint(), wind); + Wind wind = trackedRace.getWind(fix.getPosition(), toTimePointExcluding); + final SpeedWithBearing estimatedSpeed = track.getEstimatedSpeed(fix.getTimePoint()); + Tack tack = wind == null? null : trackedRace.getTack(estimatedSpeed, wind, fix.getTimePoint()); TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, fix.getTimePoint()); LegType legType; @@ -1506,7 +1507,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S legType = null; } WindDTO windDTO = wind == null ? null : createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding); - GPSFixDTO fixDTO = createGPSFixDTO(fix, track.getEstimatedSpeed(fix.getTimePoint()), windDTO, tack, legType, /* extrapolate */ + GPSFixDTO fixDTO = createGPSFixDTO(fix, estimatedSpeed, windDTO, tack, legType, /* extrapolate */ false); fixesForCompetitor.add(fixDTO); if (fixIter.hasNext()) { @@ -1516,7 +1517,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) { Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate); Wind wind2 = trackedRace.getWind(position, toTimePointExcluding); - Tack tack2 = wind2 == null ? null : trackedRace.getTack(competitor, toTimePointExcluding, wind2); + SpeedWithBearing estimatedSpeed2 = track.getEstimatedSpeed(toTimePointExcluding); + Tack tack2 = wind2 == null ? null : trackedRace.getTack(estimatedSpeed2, wind2, toTimePointExcluding); LegType legType2; if (trackedLegOfCompetitor != null && trackedLegOfCompetitor.getLeg() != null) { TimePoint quantifiedTimePoint = quantifyTimePointWithResolution( @@ -1534,12 +1536,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } else { legType2 = null; } - SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding); WindDTO windDTO2 = wind2 == null ? null : createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding); GPSFixDTO extrapolated = new GPSFixDTO( toPerCompetitorIdAsString.get(competitorDTO.getIdAsString()), position==null?null:new PositionDTO(position.getLatDeg(), position.getLngDeg()), - speedWithBearing==null?null:createSpeedWithBearingDTO(speedWithBearing), windDTO2, + estimatedSpeed2==null?null:createSpeedWithBearingDTO(estimatedSpeed2), windDTO2, tack2, legType2, /* extrapolated */ true); fixesForCompetitor.add(extrapolated); } diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index f571b5e5dae..a7f9aca43bd 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -6,6 +6,7 @@ + @@ -15,18 +16,18 @@ - + - - - - - - + + + + + + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index 8137f2e4f7a..7783b6a1741 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -19,6 +19,7 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WindSource; @@ -279,7 +280,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { } @Override - public Tack getTack(Competitor competitor, TimePoint timePoint, Wind wind) { + public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) { // TODO Auto-generated method stub return null; } From 7c73aef9173d90d6699b93fed37dbf2b213d8b52 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 26 Jun 2014 01:47:11 +0200 Subject: [PATCH 105/110] reverted launch config (removed sapjvm7 and replaced again by regular execution environment) --- java/com.sap.sailing.server/SailingServer (No Proxy).launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index a7f9aca43bd..8e810c21aad 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -16,7 +16,7 @@ - + From 33b43ffb5c207d90c436738d1b0627c6df0669d3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 26 Jun 2014 01:57:30 +0200 Subject: [PATCH 106/110] updated default env.sh to use the new garbage collector G1GC --- java/target/env.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/target/env.sh b/java/target/env.sh index 6aceeb3e1c2..376d8c01fc6 100644 --- a/java/target/env.sh +++ b/java/target/env.sh @@ -65,9 +65,9 @@ INSTALL_FROM_RELEASE= USE_ENVIRONMENT= INSTANCE_ID="$SERVER_NAME:$SERVER_PORT" -ADDITIONAL_JAVA_ARGS="-Dpersistentcompetitors.clear=false -XX:+UseMembar -XX:+UseParallelGC -XX:+UseParallelOldGC -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:logs/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" +ADDITIONAL_JAVA_ARGS="-Dpersistentcompetitors.clear=false -XX:+UseMembar -XX:+UseG1GC -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:logs/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" -JAVA_HOME=$HOME/jdk1.7.0_02 +JAVA_HOME=/opt/jdk1.7.0_60 if [[ ! -d $JAVA_HOME ]] && [[ -f "/usr/libexec/java_home" ]]; then JAVA_HOME=`/usr/libexec/java_home` fi From 749041d3e356676dc755ca7ef8590d5426275e02 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 26 Jun 2014 06:43:47 +0000 Subject: [PATCH 107/110] Created kielerwoche2014 (markdown) --- wiki/kielerwoche2014.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 wiki/kielerwoche2014.md diff --git a/wiki/kielerwoche2014.md b/wiki/kielerwoche2014.md new file mode 100644 index 00000000000..d7df1e14c43 --- /dev/null +++ b/wiki/kielerwoche2014.md @@ -0,0 +1,29 @@ +# Kieler Woche 2014 + +Two systems for seamless switching: + + - "A": ec2-54-76-190-140.eu-west-1.compute.amazonaws.com + - "B": ec2-54-76-225-59.eu-west-1.compute.amazonaws.com + +ELB-based replication cluster: + +User data for master: + +MONGODB_NAME=KW2014 +REPLICATION_CHANNEL=KW2014 +SERVER_STARTUP_NOTIFY=axel.uhl@sap.com +SERVER_NAME=KW2014 +USE_ENVIRONMENT=live-master-server +INSTALL_FROM_RELEASE=kiwo2014-release-201406241522 +MEMORY=20000m + +User data for replicas: + +MONGODB_NAME=KW2014 +REPLICATION_CHANNEL=KW2014 +SERVER_STARTUP_NOTIFY=axel.uhl@sap.com +SERVER_NAME=KW2014 +REPLICATE_MASTER_SERVLET_HOST=ec2-54-76-235-207.eu-west-1.compute.amazonaws.com +USE_ENVIRONMENT=live-replica-server +INSTALL_FROM_RELEASE=kiwo2014-release-201406260007 +MEMORY=20000m \ No newline at end of file From e41b608873cf0dd1cb6d145a50fed8bda29d1475 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 26 Jun 2014 06:54:05 +0000 Subject: [PATCH 108/110] Updated kielerwoche2014 (markdown) --- wiki/kielerwoche2014.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wiki/kielerwoche2014.md b/wiki/kielerwoche2014.md index d7df1e14c43..5b9fb076053 100644 --- a/wiki/kielerwoche2014.md +++ b/wiki/kielerwoche2014.md @@ -9,6 +9,7 @@ ELB-based replication cluster: User data for master: +

 MONGODB_NAME=KW2014
 REPLICATION_CHANNEL=KW2014
 SERVER_STARTUP_NOTIFY=axel.uhl@sap.com
@@ -16,9 +17,11 @@ SERVER_NAME=KW2014
 USE_ENVIRONMENT=live-master-server
 INSTALL_FROM_RELEASE=kiwo2014-release-201406241522
 MEMORY=20000m
+
User data for replicas: +
 MONGODB_NAME=KW2014
 REPLICATION_CHANNEL=KW2014
 SERVER_STARTUP_NOTIFY=axel.uhl@sap.com
@@ -26,4 +29,5 @@ SERVER_NAME=KW2014
 REPLICATE_MASTER_SERVLET_HOST=ec2-54-76-235-207.eu-west-1.compute.amazonaws.com
 USE_ENVIRONMENT=live-replica-server
 INSTALL_FROM_RELEASE=kiwo2014-release-201406260007
-MEMORY=20000m
\ No newline at end of file
+MEMORY=20000m
+
\ No newline at end of file From f997f2a7148d964fbcc653837ab89fb69b273b1f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 26 Jun 2014 07:00:34 +0000 Subject: [PATCH 109/110] Updated kielerwoche2014 (markdown) --- wiki/kielerwoche2014.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/kielerwoche2014.md b/wiki/kielerwoche2014.md index 5b9fb076053..40f2a705106 100644 --- a/wiki/kielerwoche2014.md +++ b/wiki/kielerwoche2014.md @@ -26,7 +26,7 @@ MONGODB_NAME=KW2014 REPLICATION_CHANNEL=KW2014 SERVER_STARTUP_NOTIFY=axel.uhl@sap.com SERVER_NAME=KW2014 -REPLICATE_MASTER_SERVLET_HOST=ec2-54-76-235-207.eu-west-1.compute.amazonaws.com +REPLICATE_MASTER_SERVLET_HOST=ec2-54-76-178-149.eu-west-1.compute.amazonaws.com USE_ENVIRONMENT=live-replica-server INSTALL_FROM_RELEASE=kiwo2014-release-201406260007 MEMORY=20000m From 24f6933e1898f78836627c2fd4590a5c275d462e Mon Sep 17 00:00:00 2001 From: fmittag Date: Thu, 26 Jun 2014 12:17:08 +0200 Subject: [PATCH 110/110] fix for bug Bug 2019 - Fleet order not preserved during series creation --- .../impl/DomainObjectFactoryImpl.java | 18 +++++------ .../sailing/domain/base/impl/SeriesImpl.java | 3 +- ...estStoringAndLoadingEventsAndRegattas.java | 30 +++++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index a9c49fc6e45..009979b0d59 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -1151,10 +1151,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries.get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name()); Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name()); final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name()); - Map fleetsByName = loadFleets(dbFleets); + List fleets = loadFleets(dbFleets); BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name()); - Iterable raceColumnNames = loadRaceColumnNames(dbRaceColumns, fleetsByName); - Series series = new SeriesImpl(name, isMedal, fleetsByName.values(), raceColumnNames, trackedRegattaRegistry); + Iterable raceColumnNames = loadRaceColumnNames(dbRaceColumns); + Series series = new SeriesImpl(name, isMedal, fleets, raceColumnNames, trackedRegattaRegistry); if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) { ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS); series.setResultDiscardingRule(resultDiscardingRule); @@ -1172,11 +1172,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { return series; } - /** - * @param fleetsByName used to ensure the {@link RaceColumn#getFleets()} points to the same {@link Fleet} objects also - * used in the {@link Series#getFleets()} collection. - */ - private Iterable loadRaceColumnNames(BasicDBList dbRaceColumns, Map fleetsByName) { + private Iterable loadRaceColumnNames(BasicDBList dbRaceColumns) { List result = new ArrayList(); for (Object o : dbRaceColumns) { DBObject dbRaceColumn = (DBObject) o; @@ -1201,12 +1197,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } } - private Map loadFleets(BasicDBList dbFleets) { - Map result = new HashMap(); + private List loadFleets(BasicDBList dbFleets) { + List result = new ArrayList(); for (Object o : dbFleets) { DBObject dbFleet = (DBObject) o; Fleet fleet = loadFleet(dbFleet); - result.put(fleet.getName(), fleet); + result.add(fleet); } return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java index 58e9272751a..10ac77b612b 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java @@ -76,7 +76,8 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe for (Fleet fleet : fleets) { this.fleetsByName.put(fleet.getName(), fleet); } - fleetsInAscendingOrder = new ArrayList(fleetsByName.values()); + fleetsInAscendingOrder = new ArrayList(); + Util.addAll(fleets, fleetsInAscendingOrder); Collections.sort(fleetsInAscendingOrder); List myRaceColumns = new ArrayList(); this.raceColumns = myRaceColumns; diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java index 0e519b69939..db247fdc853 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java @@ -468,6 +468,36 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest } } + @Test + public void testLoadStoreRegattaWithFleetsEnsuringFleetOrdering() { + final String regattaBaseName = "Kieler Woche"; + BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); + Regatta regatta = createRegatta(regattaBaseName, boatClass, + /* persistent */ false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null); + MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); + mof.storeRegatta(regatta); + + DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); + Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null); + assertEquals(regattaBaseName, loadedRegatta.getBaseName()); + + Iterator seriesIter = loadedRegatta.getSeries().iterator(); + Series loadedQualifyingSeries = seriesIter.next(); + + Iterator qualiFleetIt = loadedQualifyingSeries.getFleets().iterator(); + Fleet qualiFleet1 = qualiFleetIt.next(); + assertEquals(qualiFleet1.getName(), "Yellow"); + Fleet qualiFleet2 = qualiFleetIt.next(); + assertEquals(qualiFleet2.getName(), "Blue"); + + Series loadedFinalSeries = seriesIter.next(); + Iterator finalFleetIt = loadedFinalSeries.getFleets().iterator(); + Fleet finalFleet1 = finalFleetIt.next(); + assertEquals(finalFleet1.getName(), "Gold"); + Fleet finalFleet2 = finalFleetIt.next(); + assertEquals(finalFleet2.getName(), "Silver"); + } + @Test public void testStorageOfRaceIdentifiersOnRaceColumnInSeries() { final int numberOfQualifyingRaces = 5;