mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-11 06:40:53 +00:00
Merge branch 'master', remote-tracking branch 'origin/master' into other
This commit is contained in:
commit
68ddd22f44
987 files changed
+26277
-12723
No files matched your search
+16
-7
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import com.sap.sailing.declination.impl.DeclinationServiceImpl;
|
||||
import com.sap.sailing.declination.impl.NOAAImporter;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.Mile;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
@@ -20,11 +21,15 @@ public interface DeclinationService {
|
||||
/**
|
||||
* Obtains declination information with the default precision of this declination service in time and space
|
||||
* dimensions (e.g., one degree N/S and E/W and one year on the time axis). Particularly the time resolution may
|
||||
* benefit from an extrapolation using the {@link Declination#getAnnualChange() annual change} specification.
|
||||
* Note that the result is <em>not</em> corrected automatically by the {@link Declination#getAnnualChange()
|
||||
* annual change} of the result found for the position. The caller may do that using
|
||||
* benefit from an extrapolation using the {@link Declination#getAnnualChange() annual change} specification. Note
|
||||
* that the result is <em>not</em> corrected automatically by the {@link Declination#getAnnualChange() annual
|
||||
* change} of the result found for the position. The caller may do that using
|
||||
* {@link Declination#getBearingCorrectedTo(TimePoint)}.
|
||||
* @param timeoutForOnlineFetchInMilliseconds TODO
|
||||
*
|
||||
* @param timeoutForOnlineFetchInMilliseconds
|
||||
* see {@link NOAAImporter#getDeclination(Position, TimePoint, long)}; if 0, waits forever; only needed
|
||||
* if the pre-stored declinations don't fulfill the request up to the tolerance distance configured when
|
||||
* constructing this service
|
||||
*/
|
||||
Declination getDeclination(TimePoint timePoint, Position position, long timeoutForOnlineFetchInMilliseconds) throws IOException,
|
||||
ClassNotFoundException, ParseException;
|
||||
@@ -36,9 +41,13 @@ public interface DeclinationService {
|
||||
* <p>
|
||||
*
|
||||
* The time point precision is the default precision of this service. Note that the result is <em>not</em> corrected
|
||||
* automatically by the {@link Declination#getAnnualChange() annual change} of the result found for the
|
||||
* position. The caller may do that using {@link Declination#getBearingCorrectedTo(TimePoint)}.
|
||||
* @param timeoutForOnlineFetchInMilliseconds TODO
|
||||
* automatically by the {@link Declination#getAnnualChange() annual change} of the result found for the position.
|
||||
* The caller may do that using {@link Declination#getBearingCorrectedTo(TimePoint)}.
|
||||
*
|
||||
* @param timeoutForOnlineFetchInMilliseconds
|
||||
* see {@link NOAAImporter#getDeclination(Position, TimePoint, long)}; if 0, waits forever; only needed
|
||||
* if the pre-stored declinations don't fulfill the request up to the tolerance distance provided by the
|
||||
* <code>maxDistance</code> parameter
|
||||
*/
|
||||
Declination getDeclination(TimePoint timePoint, Position position, Distance maxDistance, long timeoutForOnlineFetchInMilliseconds) throws IOException,
|
||||
ClassNotFoundException, ParseException;
|
||||
|
||||
@@ -7,6 +7,8 @@ Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.common.configuration,
|
||||
com.sap.sailing.domain.common.dto,
|
||||
com.sap.sailing.domain.common.filter,
|
||||
com.sap.sailing.domain.common.impl,
|
||||
com.sap.sailing.domain.common.media,
|
||||
@@ -14,5 +16,4 @@ Export-Package: com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.common.quadtree.impl;x-friends:="com.sap.sailing.domain.test",
|
||||
com.sap.sailing.domain.common.racelog,
|
||||
com.sap.sailing.domain.common.racelog.utils,
|
||||
com.sap.sailing.domain.common.dto,
|
||||
com.sap.sailing.domain.common.trackfiles
|
||||
@@ -13,6 +13,10 @@ public interface Bearing extends Serializable {
|
||||
*/
|
||||
Bearing reverse();
|
||||
|
||||
/**
|
||||
* Adds the bearing <code>diff</code> to this bearing and returns the new result. The result will be between 0 and 360 (inclusive) if
|
||||
* this bearing and <code>diff</code> are also within 0 and 360.
|
||||
*/
|
||||
Bearing add(Bearing diff);
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,36 @@ package com.sap.sailing.domain.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.RGBColor;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
|
||||
public interface Color extends Serializable {
|
||||
final Color WHITE = new RGBColor(255, 255, 255);
|
||||
|
||||
final Color LIGHT_GRAY = new RGBColor(192, 192, 192);
|
||||
|
||||
final Color GRAY = new RGBColor(128, 128, 128);
|
||||
|
||||
final Color DARK_GRAY = new RGBColor(64, 64, 64);
|
||||
|
||||
final Color BLACK = new RGBColor(0, 0, 0);
|
||||
|
||||
final Color RED = new RGBColor(255, 0, 0);
|
||||
|
||||
final Color PINK = new RGBColor(255, 175, 175);
|
||||
|
||||
final Color ORANGE = new RGBColor(255, 200, 0);
|
||||
|
||||
final Color YELLOW = new RGBColor(255, 255, 0);
|
||||
|
||||
final Color GREEN = new RGBColor(0, 255, 0);
|
||||
|
||||
final Color MAGENTA = new RGBColor(255, 0, 255);
|
||||
|
||||
final Color CYAN = new RGBColor(0, 255, 255);
|
||||
|
||||
final Color BLUE = new RGBColor(0, 0, 255);
|
||||
|
||||
Util.Triple<Integer, Integer, Integer> getAsRGB();
|
||||
|
||||
Util.Triple<Float, Float, Float> getAsHSV();
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
/**
|
||||
* Manages color assignments to objects.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
* @param <T>
|
||||
* the type of the objects to which a color is assigned
|
||||
*/
|
||||
public interface ColorMap<T> {
|
||||
Color getColorByID(T object);
|
||||
|
||||
boolean addBlockedColor(Color color);
|
||||
boolean removeBlockedColor(Color color);
|
||||
void clearBlockedColors();
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to decided which Course Designer to pick.
|
||||
*
|
||||
* When modifying these values also check res/preferences.xml of racecommittee.app!
|
||||
*/
|
||||
public enum CourseDesignerMode {
|
||||
UNKNOWN("Unknown"),
|
||||
BY_NAME("By-Name Course Designer"), // O2, O3, I2,...
|
||||
BY_MAP("By-Map Course Designer"), // Map-based Course Designer
|
||||
BY_MARKS("By-Marks Course Designer"); // ESS
|
||||
|
||||
private String displayName;
|
||||
|
||||
private CourseDesignerMode(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public static CourseDesignerMode[] validValues() {
|
||||
List<CourseDesignerMode> validValues = new ArrayList<CourseDesignerMode>();
|
||||
for (CourseDesignerMode type : values()) {
|
||||
if (type != UNKNOWN) {
|
||||
validValues.add(type);
|
||||
}
|
||||
}
|
||||
return validValues.toArray(new CourseDesignerMode[0]);
|
||||
}
|
||||
}
|
||||
+4
@@ -29,4 +29,8 @@ public interface SpeedWithBearing extends Speed {
|
||||
*/
|
||||
Speed projectTo(Position position, Bearing bearing);
|
||||
|
||||
/**
|
||||
* Adds two directed speeds onto each other using vector addition.
|
||||
*/
|
||||
SpeedWithBearing add(SpeedWithBearing other);
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.sap.sailing.domain.common.configuration;
|
||||
|
||||
public enum DeviceConfigurationMatcherType {
|
||||
|
||||
SINGLE(1), MULTI(2);
|
||||
|
||||
private int matchingRank;
|
||||
|
||||
private DeviceConfigurationMatcherType(int rank) {
|
||||
this.matchingRank = rank;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return matchingRank;
|
||||
}
|
||||
}
|
||||
+4
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
|
||||
public interface CompetitorDTO extends Serializable {
|
||||
|
||||
String getTwoLetterIsoCountryCode();
|
||||
@@ -17,6 +19,8 @@ public interface CompetitorDTO extends Serializable {
|
||||
BoatClassDTO getBoatClass();
|
||||
|
||||
String getName();
|
||||
|
||||
Color getColor();
|
||||
|
||||
/**
|
||||
* A regular instance will simply return this object. A compacted version may compute the result by looking it up
|
||||
|
||||
+15
-2
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
|
||||
/**
|
||||
* Equality and hash code are based on the {@link #getIdAsString() ID}, the {@link #getSailID() sail number}, the
|
||||
* {@link #getBoatClass() boat class} (whose equality and hash code, in turn, depends on its name) and the
|
||||
@@ -13,18 +15,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serializable {
|
||||
private static final long serialVersionUID = -4997852354821083154L;
|
||||
private String countryName;
|
||||
private String twoLetterIsoCountryCode;
|
||||
private String threeLetterIocCountryCode;
|
||||
private String countryName;
|
||||
private Color color;
|
||||
private String sailID;
|
||||
private String idAsString;
|
||||
private BoatClassDTO boatClass;
|
||||
|
||||
CompetitorDTOImpl() {}
|
||||
|
||||
public CompetitorDTOImpl(String name, String twoLetterIsoCountryCode, String threeLetterIocCountryCode,
|
||||
public CompetitorDTOImpl(String name, Color color, String twoLetterIsoCountryCode, String threeLetterIocCountryCode,
|
||||
String countryName, String sailID, String idAsString, BoatClassDTO boatClass) {
|
||||
super(name);
|
||||
this.color = color;
|
||||
this.twoLetterIsoCountryCode = twoLetterIsoCountryCode;
|
||||
this.threeLetterIocCountryCode = threeLetterIocCountryCode;
|
||||
this.countryName = countryName;
|
||||
@@ -40,6 +44,7 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
|
||||
result = prime * result + ((boatClass == null) ? 0 : boatClass.hashCode());
|
||||
result = prime * result + ((idAsString == null) ? 0 : idAsString.hashCode());
|
||||
result = prime * result + ((sailID == null) ? 0 : sailID.hashCode());
|
||||
result = prime * result + ((color == null) ? 0 : color.hashCode());
|
||||
result = prime * result + ((threeLetterIocCountryCode == null) ? 0 : threeLetterIocCountryCode.hashCode());
|
||||
return result;
|
||||
}
|
||||
@@ -73,6 +78,11 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
|
||||
return false;
|
||||
} else if (!threeLetterIocCountryCode.equals(other.threeLetterIocCountryCode))
|
||||
return false;
|
||||
if (color == null) {
|
||||
if (other.color != null)
|
||||
return false;
|
||||
} else if (!color.equals(other.color))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -111,4 +121,7 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
|
||||
return this;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
+8
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.domain.common.dto;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
|
||||
/**
|
||||
* Equality and hash code are based on the index pointing into a previous leaderboard's competitors list.
|
||||
*
|
||||
@@ -67,6 +69,12 @@ public class PreviousCompetitorDTOImpl implements CompetitorDTO {
|
||||
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor() {
|
||||
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
|
||||
" need to be replaced by an object of "+CompetitorDTOImpl.class.getName()+" after deserialization");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdAsString() {
|
||||
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sailing.domain.common.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
|
||||
public abstract class AbstractColor implements Color {
|
||||
private static final long serialVersionUID = 7758884012281863458L;
|
||||
|
||||
@Override
|
||||
public String getAsHtml() {
|
||||
Triple<Integer, Integer, Integer> asRGB = getAsRGB();
|
||||
return "#" + toBrowserHexValue(asRGB.getA()) + toBrowserHexValue(asRGB.getB())
|
||||
+ toBrowserHexValue(asRGB.getC());
|
||||
}
|
||||
|
||||
private static String toBrowserHexValue(int number) {
|
||||
StringBuilder builder = new StringBuilder(Integer.toHexString(number & 0xff));
|
||||
while (builder.length() < 2) {
|
||||
builder.append("0");
|
||||
}
|
||||
return builder.toString().toUpperCase();
|
||||
}
|
||||
}
|
||||
+13
@@ -33,6 +33,11 @@ public abstract class AbstractSpeedWithAbstractBearingImpl extends AbstractSpeed
|
||||
&& getBearing().equals(((SpeedWithBearing) object).getBearing());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing add(SpeedWithBearing other) {
|
||||
return AbstractSpeedWithAbstractBearingImpl.add(this, other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CourseChange getCourseChangeRequiredToReach(SpeedWithBearing targetSpeedWithBearing) {
|
||||
return AbstractSpeedWithBearingImpl.getCourseChangeRequiredToReach(this, targetSpeedWithBearing);
|
||||
@@ -80,4 +85,12 @@ public abstract class AbstractSpeedWithAbstractBearingImpl extends AbstractSpeed
|
||||
public Speed projectTo(Position position, Bearing projectTo) {
|
||||
return projectTo(this, position, projectTo);
|
||||
}
|
||||
|
||||
public static SpeedWithBearing add(SpeedWithBearing first, SpeedWithBearing other) {
|
||||
double x = first.getMetersPerSecond()*Math.cos(first.getBearing().getRadians()) + other.getMetersPerSecond()*Math.cos(other.getBearing().getRadians());
|
||||
double y = first.getMetersPerSecond()*Math.sin(first.getBearing().getRadians()) + other.getMetersPerSecond()*Math.sin(other.getBearing().getRadians());
|
||||
double metersPerSecond = Math.sqrt(x*x+y*y);
|
||||
double directionRad = (360.+Math.atan2(y, x))%360.;
|
||||
return new MeterPerSecondSpeedWithDegreeBearingImpl(metersPerSecond, new RadianBearingImpl(directionRad));
|
||||
}
|
||||
}
|
||||
-1
@@ -15,5 +15,4 @@ public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedWithAbst
|
||||
public Bearing getBearing() {
|
||||
return bearing;
|
||||
}
|
||||
|
||||
}
|
||||
+42
-11
@@ -1,8 +1,13 @@
|
||||
package com.sap.sailing.gwt.ui.client;
|
||||
package com.sap.sailing.domain.common.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
import com.sap.sailing.domain.common.ColorMap;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.HSVColor;
|
||||
|
||||
/**
|
||||
* Manages color assignments to objects.
|
||||
@@ -12,14 +17,17 @@ import com.sap.sailing.domain.common.impl.HSVColor;
|
||||
* @param <T>
|
||||
* the type of the objects to which a color is assigned
|
||||
*/
|
||||
public class ColorMap<T> {
|
||||
private final HashMap<T, String> idColor;
|
||||
public class ColorMapImpl<T> implements ColorMap<T> {
|
||||
private final HashMap<T, Color> idColor;
|
||||
|
||||
private int colorCounter;
|
||||
|
||||
private HSVColor[] baseColors;
|
||||
|
||||
public ColorMap() {
|
||||
/** a list of already used colors which should be excluded from the automatic color assignment */
|
||||
private List<HSVColor> blockedColors;
|
||||
|
||||
public ColorMapImpl() {
|
||||
baseColors = new HSVColor[10];
|
||||
baseColors[0] = new HSVColor(0.0f, 1.0f, 1.0f); // Red
|
||||
baseColors[1] = new HSVColor(30.0f, 1.0f, 1.0f); // Orange
|
||||
@@ -32,7 +40,8 @@ public class ColorMap<T> {
|
||||
baseColors[8] = new HSVColor(300.0f, 1.0f, 1.0f); // Magenta
|
||||
baseColors[9] = new HSVColor(330.0f, 1.0f, 1.0f);
|
||||
|
||||
idColor = new HashMap<T, String>();
|
||||
idColor = new HashMap<T, Color>();
|
||||
blockedColors = new ArrayList<HSVColor>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,15 +52,37 @@ public class ColorMap<T> {
|
||||
* object's {@link Object#equals(Object)} and {@link Object#hashCode()}.
|
||||
* @return A color in hex/html-format (e.g. #ff0000)
|
||||
*/
|
||||
public String getColorByID(T object) {
|
||||
String color = idColor.get(object);
|
||||
if (color == null || color.isEmpty()) {
|
||||
public Color getColorByID(T object) {
|
||||
Color color = idColor.get(object);
|
||||
if (color == null) {
|
||||
color = createHexColor(colorCounter++);
|
||||
idColor.put(object, color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
public boolean addBlockedColor(Color color) {
|
||||
boolean result = false;
|
||||
if(color != null) {
|
||||
Triple<Float, Float, Float> asHSV = color.getAsHSV();
|
||||
result = blockedColors.add(new HSVColor(asHSV.getA(), asHSV.getB(), asHSV.getC()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean removeBlockedColor(Color color) {
|
||||
boolean result = false;
|
||||
if(color != null) {
|
||||
Triple<Float, Float, Float> asHSV = color.getAsHSV();
|
||||
result = blockedColors.remove(new HSVColor(asHSV.getA(), asHSV.getB(), asHSV.getC()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void clearBlockedColors() {
|
||||
blockedColors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use this if you don't want the color to be cached.
|
||||
*
|
||||
@@ -59,7 +90,7 @@ public class ColorMap<T> {
|
||||
* The index of e.g. a competitor. Make sure, that each competitor has a unique index.
|
||||
* @return A color computed using the {@code index}.
|
||||
*/
|
||||
private String createHexColor(int index) {
|
||||
private Color createHexColor(int index) {
|
||||
int baseColorCount = baseColors.length;
|
||||
int baseColorsIndex = index % baseColorCount;
|
||||
int factor = index / (baseColorCount*3);
|
||||
@@ -87,6 +118,6 @@ public class ColorMap<T> {
|
||||
HSVColor hsvColor = baseColors[baseColorsIndex];
|
||||
HSVColor newColor = new HSVColor(hsvColor.getHue(), hsvColor.getSaturation() - saturationDecrease,
|
||||
hsvColor.getBrightness() - brightnessDecrease);
|
||||
return newColor.getAsHtml();
|
||||
return newColor;
|
||||
}
|
||||
}
|
||||
+32
-20
@@ -1,6 +1,5 @@
|
||||
package com.sap.sailing.domain.common.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
|
||||
/**
|
||||
@@ -9,12 +8,12 @@ import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
*
|
||||
* @author Frank
|
||||
*/
|
||||
public class HSVColor implements Color {
|
||||
public class HSVColor extends AbstractColor {
|
||||
private static final long serialVersionUID = 7602013229606352246L;
|
||||
|
||||
float hue;
|
||||
float saturation;
|
||||
float brightness;
|
||||
protected float hue;
|
||||
protected float saturation;
|
||||
protected float brightness;
|
||||
|
||||
HSVColor() {
|
||||
} // for GWT serializability
|
||||
@@ -86,21 +85,6 @@ public class HSVColor implements Color {
|
||||
return RGBColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsHtml() {
|
||||
Triple<Integer, Integer, Integer> asRGB = getAsRGB();
|
||||
return "#" + toBrowserHexValue(asRGB.getA()) + toBrowserHexValue(asRGB.getB())
|
||||
+ toBrowserHexValue(asRGB.getC());
|
||||
}
|
||||
|
||||
private static String toBrowserHexValue(int number) {
|
||||
StringBuilder builder = new StringBuilder(Integer.toHexString(number & 0xff));
|
||||
while (builder.length() < 2) {
|
||||
builder.append("0");
|
||||
}
|
||||
return builder.toString().toUpperCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Triple<Float, Float, Float> getAsHSV() {
|
||||
return new Triple<Float, Float, Float>(hue, saturation, brightness);
|
||||
@@ -122,4 +106,32 @@ public class HSVColor implements Color {
|
||||
public float getBrightness() {
|
||||
return brightness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Float.floatToIntBits(brightness);
|
||||
result = prime * result + Float.floatToIntBits(hue);
|
||||
result = prime * result + Float.floatToIntBits(saturation);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
HSVColor other = (HSVColor) obj;
|
||||
if (Float.floatToIntBits(brightness) != Float.floatToIntBits(other.brightness))
|
||||
return false;
|
||||
if (Float.floatToIntBits(hue) != Float.floatToIntBits(other.hue))
|
||||
return false;
|
||||
if (Float.floatToIntBits(saturation) != Float.floatToIntBits(other.saturation))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.sap.sailing.domain.common.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
|
||||
public class QRCodeUtils {
|
||||
|
||||
private static final String fragmentKey = "identifier";
|
||||
|
||||
public static String composeQRContent(String deviceIdentifier, String apkUrl) {
|
||||
// poor man's uri fragment encoding: ' ' as '%20'
|
||||
String encodedIdentifier = deviceIdentifier.replaceAll(" ", "%20");
|
||||
return apkUrl + "#" + fragmentKey + "=" + encodedIdentifier;
|
||||
}
|
||||
|
||||
public static Pair<String, String> splitQRContent(String qrCodeContent) {
|
||||
int fragmentIndex = qrCodeContent.lastIndexOf('#');
|
||||
if (fragmentIndex == -1 || fragmentIndex == 0 || qrCodeContent.length() == fragmentIndex + 1) {
|
||||
throw new IllegalArgumentException("There is no server or identifier.");
|
||||
}
|
||||
String deviceIdentifier = qrCodeContent.substring(fragmentIndex + 1, qrCodeContent.length());
|
||||
if (!deviceIdentifier.startsWith(fragmentKey + "=")) {
|
||||
throw new IllegalArgumentException("The identifier is malformed");
|
||||
}
|
||||
deviceIdentifier = deviceIdentifier.substring((fragmentKey + "=").length());
|
||||
deviceIdentifier = deviceIdentifier.replaceAll("%20", " ");
|
||||
|
||||
String apkUrl = qrCodeContent.substring(0, fragmentIndex);
|
||||
return new Pair<String, String>(deviceIdentifier, apkUrl);
|
||||
}
|
||||
|
||||
}
|
||||
+35
-15
@@ -1,13 +1,12 @@
|
||||
package com.sap.sailing.domain.common.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
|
||||
/**
|
||||
* A color defined in the RGB color schema
|
||||
* @author Frank
|
||||
*/
|
||||
public class RGBColor implements Color {
|
||||
public class RGBColor extends AbstractColor {
|
||||
private static final long serialVersionUID = -4091876840771631308L;
|
||||
private int red;
|
||||
private int green;
|
||||
@@ -21,6 +20,40 @@ public class RGBColor implements Color {
|
||||
this.green = ensureValidRange(green);
|
||||
this.blue = ensureValidRange(blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RGB color from a string such as "#FF00FF"
|
||||
*/
|
||||
public RGBColor(String htmlColor) {
|
||||
if (htmlColor.startsWith("#")) {
|
||||
htmlColor = htmlColor.substring(1);
|
||||
}
|
||||
if (htmlColor.endsWith(";")) {
|
||||
htmlColor = htmlColor.substring(0, htmlColor.length() - 1);
|
||||
}
|
||||
|
||||
int r, g, b;
|
||||
switch (htmlColor.length()) {
|
||||
case 6:
|
||||
r = Integer.parseInt(htmlColor.substring(0, 2), 16);
|
||||
g = Integer.parseInt(htmlColor.substring(2, 4), 16);
|
||||
b = Integer.parseInt(htmlColor.substring(4, 6), 16);
|
||||
break;
|
||||
case 3:
|
||||
r = Integer.parseInt(htmlColor.substring(0, 1), 16);
|
||||
g = Integer.parseInt(htmlColor.substring(1, 2), 16);
|
||||
b = Integer.parseInt(htmlColor.substring(2, 3), 16);
|
||||
break;
|
||||
case 1:
|
||||
r = g = b = Integer.parseInt(htmlColor.substring(0, 1), 16);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(htmlColor);
|
||||
}
|
||||
this.red = ensureValidRange(r);
|
||||
this.green = ensureValidRange(g);
|
||||
this.blue = ensureValidRange(b);
|
||||
}
|
||||
|
||||
private int ensureValidRange(int value) {
|
||||
int result = value;
|
||||
@@ -78,19 +111,6 @@ public class RGBColor implements Color {
|
||||
return HSVColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsHtml() {
|
||||
return "#" + toBrowserHexValue(red) + toBrowserHexValue(green) + toBrowserHexValue(blue);
|
||||
}
|
||||
|
||||
private static String toBrowserHexValue(int number) {
|
||||
StringBuilder builder = new StringBuilder(Integer.toHexString(number & 0xff));
|
||||
while (builder.length() < 2) {
|
||||
builder.append("0");
|
||||
}
|
||||
return builder.toString().toUpperCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sailing.domain.common.racelog;
|
||||
|
||||
public class FlagPole {
|
||||
|
||||
private final Flags upperFlag;
|
||||
private final Flags lowerFlag;
|
||||
private final boolean isDisplayed;
|
||||
|
||||
public FlagPole(Flags flag, boolean isDisplayed) {
|
||||
this(flag, Flags.NONE, isDisplayed);
|
||||
}
|
||||
|
||||
public FlagPole(Flags upperFlag, Flags lowerFlag, boolean isDisplayed) {
|
||||
this.upperFlag = upperFlag;
|
||||
this.lowerFlag = lowerFlag;
|
||||
this.isDisplayed = isDisplayed;
|
||||
}
|
||||
|
||||
public Flags getUpperFlag() {
|
||||
return upperFlag;
|
||||
}
|
||||
|
||||
public Flags getLowerFlag() {
|
||||
return lowerFlag;
|
||||
}
|
||||
|
||||
public boolean isDisplayed() {
|
||||
return isDisplayed;
|
||||
}
|
||||
|
||||
public boolean describesSame(FlagPole otherPole) {
|
||||
return this.upperFlag == otherPole.upperFlag &&
|
||||
this.lowerFlag == otherPole.lowerFlag &&
|
||||
this.isDisplayed == otherPole.isDisplayed;
|
||||
}
|
||||
|
||||
}
|
||||
+14
-1
@@ -1,5 +1,8 @@
|
||||
package com.sap.sailing.domain.common.racelog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public enum Flags {
|
||||
NONE,
|
||||
ALPHA,
|
||||
@@ -20,5 +23,15 @@ public enum Flags {
|
||||
AP,
|
||||
ESSONE,
|
||||
ESSTWO,
|
||||
ESSTHREE
|
||||
ESSTHREE;
|
||||
|
||||
public static Flags[] validValues() {
|
||||
List<Flags> validValues = new ArrayList<Flags>();
|
||||
for (Flags flag : values()) {
|
||||
if (flag != NONE) {
|
||||
validValues.add(flag);
|
||||
}
|
||||
}
|
||||
return validValues.toArray(new Flags[validValues.size()]);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.sap.sailing.domain.common.racelog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to identify a RacingProcedure's type.
|
||||
*
|
||||
* When modifying these values also check res/preferences.xml of racecommittee.app!
|
||||
*/
|
||||
public enum RacingProcedureType {
|
||||
UNKNOWN("Unknown"),
|
||||
RRS26("Fix Line Start (RRS26)"),
|
||||
GateStart("Gate Start"),
|
||||
ESS("\"Extreme Sailing Series\"-Start"),
|
||||
BASIC("Basic Countdown Start");
|
||||
|
||||
private String displayName;
|
||||
|
||||
private RacingProcedureType(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public static RacingProcedureType[] validValues() {
|
||||
List<RacingProcedureType> validValues = new ArrayList<RacingProcedureType>();
|
||||
for (RacingProcedureType type : values()) {
|
||||
if (type != UNKNOWN) {
|
||||
validValues.add(type);
|
||||
}
|
||||
}
|
||||
return validValues.toArray(new RacingProcedureType[0]);
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package com.sap.sailing.domain.common.racelog;
|
||||
|
||||
public enum StartProcedureType {
|
||||
RRS26("Fix Line Start (RRS26)"),
|
||||
GateStart("Gate Start"),
|
||||
ESS("\"Extreme Sailing Series\"-Start");
|
||||
|
||||
private String displayName;
|
||||
|
||||
private StartProcedureType(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
-1
@@ -2,7 +2,6 @@ package com.sap.sailing.domain.igtimiadapter.persistence.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
|
||||
+22
-8
@@ -58,20 +58,34 @@ public class FixFactoryTest {
|
||||
"},\n"+
|
||||
"\"13:100\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"60\n"+
|
||||
"23766\n"+
|
||||
"24619\n"+
|
||||
"],\n"+
|
||||
"\"2\": [\n"+
|
||||
"105\n"+
|
||||
"106\n"+
|
||||
"],\n"+
|
||||
"\"3\": [\n"+
|
||||
"72\n"+
|
||||
"72\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345678000\n"+
|
||||
"1385628380500\n"+
|
||||
"1385628381000\n"+
|
||||
"]\n"+
|
||||
"},\n"+
|
||||
"\"13:201\": {\n"+
|
||||
"\"1\": [\n"+
|
||||
"119,\n"+
|
||||
"120\n"+
|
||||
"24619\n"+
|
||||
"],\n"+
|
||||
"\"2\": [\n"+
|
||||
"106\n"+
|
||||
"],\n"+
|
||||
"\"3\": [\n"+
|
||||
"119\n"+
|
||||
"],\n"+
|
||||
"\"t\": [\n"+
|
||||
"12345677000,\n"+
|
||||
"12345677500\n"+
|
||||
"1385628381000\n"+
|
||||
"]\n"+
|
||||
"}\n"+
|
||||
"}\n"+
|
||||
@@ -102,7 +116,7 @@ public class FixFactoryTest {
|
||||
assertEquals(21, satCount.getSatCount());
|
||||
fixIter.next();
|
||||
AntHrm hrm = (AntHrm) fixIter.next();
|
||||
assertEquals("100", hrm.getSensor().getDeviceId());
|
||||
assertEquals(60, hrm.getHeartRate());
|
||||
assertEquals(100, hrm.getSensor().getSensorId());
|
||||
assertEquals(72, hrm.getHeartRate());
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -11,7 +11,6 @@ import org.json.simple.parser.ParseException;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory;
|
||||
import com.sap.sailing.mongodb.MongoDBConfiguration;
|
||||
import com.sap.sailing.mongodb.MongoDBService;
|
||||
@@ -23,7 +22,7 @@ public class WebSocketServerListTest {
|
||||
"aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback");
|
||||
MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration();
|
||||
MongoDBService mongoTestService = mongoTestConfig.getService();
|
||||
final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoTestService));
|
||||
Iterable<URI> serverUris = igtimiConnectionFactory.getWebsocketServers();
|
||||
assertFalse(Util.isEmpty(serverUris));
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
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.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.COG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.SOG;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindListener;
|
||||
|
||||
public class WindReceiverTest {
|
||||
@Test
|
||||
public void simpleWindReceiverTest() {
|
||||
final List<Wind> windReceived = new ArrayList<>();
|
||||
final String deviceSerialNumber = "Non-Existing Test Device";
|
||||
IgtimiWindReceiver receiver = new IgtimiWindReceiver(deviceSerialNumber);
|
||||
receiver.addListener(new WindListener() {
|
||||
@Override
|
||||
public void windDataReceived(Wind wind) {
|
||||
windReceived.add(wind);
|
||||
}
|
||||
@Override
|
||||
public void windDataRemoved(Wind wind) {}
|
||||
@Override
|
||||
public void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {}
|
||||
});
|
||||
TimePoint timePoint = MillisecondsTimePoint.now();
|
||||
Map<Integer, Object> awaMap = new HashMap<>(); awaMap.put(1, 123. /* degrees from */);
|
||||
Map<Integer, Object> awsMap = new HashMap<>(); awsMap.put(1, 12. /* knots */);
|
||||
Map<Integer, Object> hdgMap = new HashMap<>(); hdgMap.put(1, 20. /* true degrees */);
|
||||
Map<Integer, Object> gpsLatLongMap = new HashMap<>(); gpsLatLongMap.put(2, 49.8 /* lat */); gpsLatLongMap.put(1, 8.93 /* lng */);
|
||||
Map<Integer, Object> sogMap = new HashMap<>(); sogMap.put(1, 10. /* knots */);
|
||||
Map<Integer, Object> cogMap = new HashMap<>(); cogMap.put(1, 22. /* degrees; 2 degrees drift */);
|
||||
final SensorImpl sensor = new SensorImpl(deviceSerialNumber, 0);
|
||||
SpeedWithBearing boatSogCog = new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(22));
|
||||
SpeedWithBearing apparentWind = new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(20).add(new DegreeBearingImpl(123).reverse()));
|
||||
SpeedWithBearing expectedTrueWind = apparentWind.add(boatSogCog);
|
||||
receiver.received(Arrays.asList(new AWA(timePoint, sensor, awaMap),
|
||||
new AWS(timePoint, sensor, awsMap),
|
||||
new HDG(timePoint, sensor, hdgMap),
|
||||
new GpsLatLong(timePoint, sensor, gpsLatLongMap),
|
||||
new SOG(timePoint, sensor, sogMap),
|
||||
new COG(timePoint, sensor, cogMap)));
|
||||
assertFalse(windReceived.isEmpty());
|
||||
assertEquals(1, windReceived.size());
|
||||
Wind wind = windReceived.get(0);
|
||||
assertEquals(49.8, wind.getPosition().getLatDeg(), 0.00000001);
|
||||
assertEquals(8.93, wind.getPosition().getLngDeg(), 0.00000001);
|
||||
assertEquals(expectedTrueWind.getKnots(), wind.getKnots(), 0.00000001);
|
||||
assertEquals(expectedTrueWind.getBearing().getDegrees(), wind.getBearing().getDegrees(), 0.00000001);
|
||||
}
|
||||
}
|
||||
+19
-47
@@ -3,12 +3,8 @@ package com.sap.sailing.domain.igtimiadapter.oauth;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
@@ -18,17 +14,9 @@ import org.json.simple.parser.ParseException;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.Client;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.ClientImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectionFactoryImpl;
|
||||
@@ -48,7 +36,7 @@ public class SignInTest {
|
||||
"aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback");
|
||||
MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration();
|
||||
MongoDBService mongoTestService = mongoTestConfig.getService();
|
||||
final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoTestService));
|
||||
final String code = igtimiConnectionFactory.authorizeAndReturnAuthorizedCode("axel.uhl@gmx.de", "123456");
|
||||
logger.info("Igtimi OAuth code is "+code);
|
||||
@@ -66,51 +54,35 @@ public class SignInTest {
|
||||
"4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http://1.2.3.4");
|
||||
MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration();
|
||||
MongoDBService mongoTestService = mongoTestConfig.getService();
|
||||
final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoTestService));
|
||||
final String code = igtimiConnectionFactory.authorizeAndReturnAuthorizedCode("axel.uhl@gmx.de", "123456");
|
||||
logger.info("Igtimi OAuth code is "+code);
|
||||
assertNotNull(code);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAuthorize() throws ClientProtocolException, IOException,
|
||||
IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException,
|
||||
InstantiationException, IllegalAccessException, ClassCastException, ParseException {
|
||||
// use the credentials of "Another Test App"
|
||||
final Client testAppClient = new ClientImpl("a4cecd8593e12d43a03433a6db0eea243a411749f93c278dce6a26d4804eebd2",
|
||||
"4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http://1.2.3.4");
|
||||
MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration();
|
||||
MongoDBService mongoTestService = mongoTestConfig.getService();
|
||||
final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoTestService));
|
||||
final Account account = igtimiConnectionFactory.createAccountToAccessUserData("axel.uhl@gmx.de", "123456");
|
||||
assertNotNull(account);
|
||||
logger.info("Igtimi account is "+account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddToken() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
assertEquals("axel.uhl@gmx.de", account.getUser().getEmail());
|
||||
assertSame(account, connectionFactory.getAccountByEmail("axel.uhl@gmx.de"));
|
||||
assertSame(account, connectionFactory.getExistingAccountByEmail("axel.uhl@gmx.de"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUsers() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Iterable<User> users = connection.getUsers();
|
||||
assertEquals(1, Util.size(users));
|
||||
assertEquals(account.getUser().getId(), users.iterator().next().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResources() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Iterable<Resource> resources = connection.getResources(Permission.read, /* start time */ null, /* end time */ null,
|
||||
/* serial numbers */ Collections.singleton("GA-EN-AAEJ"), /* stream IDs */ null);
|
||||
assertTrue(resources.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceData() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Map<Type, Double> typesAndCompression = new HashMap<>();
|
||||
typesAndCompression.put(Type.gps_latlong, 0.0);
|
||||
Iterable<Fix> data = connection.getResourceData(new MillisecondsTimePoint(1384420883000l),
|
||||
new MillisecondsTimePoint(1384421639000l), Collections.singleton("GA-EN-AAEJ"), typesAndCompression);
|
||||
assertTrue(data.iterator().hasNext());
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.junit.Before;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
|
||||
|
||||
public class AbstractTestWithIgtimiConnection {
|
||||
protected IgtimiConnection connection;
|
||||
|
||||
@Before
|
||||
public void setUp() throws ClientProtocolException, IOException, org.json.simple.parser.ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
connection = connectionFactory.connect(account);
|
||||
}
|
||||
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
|
||||
import com.sap.sailing.domain.igtimiadapter.Group;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
import com.sap.sailing.domain.igtimiadapter.Session;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
|
||||
|
||||
public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
|
||||
@Test
|
||||
public void testGetUsers() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Iterable<User> users = connection.getUsers();
|
||||
assertEquals(1, Util.size(users));
|
||||
assertEquals(connection.getAccount().getUser().getId(), users.iterator().next().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGroups() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Iterable<Group> groups = connection.getGroups();
|
||||
assertTrue(Util.size(groups) > 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDataAccessWindows() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, /* startTime */ null,
|
||||
/* endTime */ null, /* deviceSerialNumbers */ Collections.singleton("DD-EE-AAGA"));
|
||||
assertFalse(Util.isEmpty(daws));
|
||||
for (DataAccessWindow daw : daws) {
|
||||
assertEquals("DD-EE-AAGA", daw.getDeviceSerialNumber());
|
||||
assertTrue(daw.getRecipient() instanceof Group);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllDataAccessWindows() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, /* startTime */ null,
|
||||
/* endTime */ null, /* deviceSerialNumbers */ null);
|
||||
assertFalse(Util.isEmpty(daws));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResources() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Iterable<Resource> resources = connection.getResources(Permission.read, /* start time */ null, /* end time */ null,
|
||||
/* serial numbers */ Collections.singleton("GA-EN-AAEJ"), /* stream IDs */ null);
|
||||
assertTrue(resources.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSessions() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Iterable<Session> sessions = connection.getSessions(Collections.singleton(4571l), /* isPublic */ null, /* limit */ 1, /* includeIncomplete */ null);
|
||||
assertTrue(sessions.iterator().hasNext());
|
||||
final Session session = sessions.iterator().next();
|
||||
assertEquals("Auto Session - 01:14:02 10 Nov", session.getName());
|
||||
User sessionOwher = connection.getUser(session.getOwnerId());
|
||||
assertEquals("Uhl", sessionOwher.getSurname());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceData() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
Map<Type, Double> typesAndCompression = new HashMap<>();
|
||||
typesAndCompression.put(Type.gps_latlong, 0.0);
|
||||
Iterable<Fix> data = connection.getResourceData(new MillisecondsTimePoint(1384420883000l),
|
||||
new MillisecondsTimePoint(1384421639000l), Collections.singleton("GA-EN-AAEJ"), typesAndCompression);
|
||||
assertTrue(data.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataAccessWindowForGivenTimeFrame() throws java.text.ParseException, IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
|
||||
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2013-11-07T08:00:00Z"));
|
||||
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T18:00:00Z"));
|
||||
// URL is https://www.igtimi.com/api/v1/devices/data_access_windows?type=read&start_time=1383811200000&end_time=1383933600000&access_token=3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9
|
||||
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
|
||||
assertFalse(Util.isEmpty(daws));
|
||||
for (DataAccessWindow daw : daws) {
|
||||
assertTrue((daw.getStartTime().compareTo(start)<=0 && daw.getEndTime().compareTo(start)>0) // spans start
|
||||
|| (daw.getStartTime().compareTo(end)<=0 && daw.getEndTime().compareTo(end)>0) // spans end
|
||||
|| (daw.getStartTime().compareTo(start)>=0 && daw.getEndTime().compareTo(end)<=0)); // lies within
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceDataForGivenTimeFrame() throws java.text.ParseException, IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
|
||||
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:00:00Z"));
|
||||
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:10:00Z"));
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
// URL is https://www.igtimi.com/api/v1/devices/data_access_windows?type=read&start_time=1383811200000&end_time=1383933600000&access_token=3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9
|
||||
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
|
||||
Set<String> deviceSerialNumbers = new HashSet<>();
|
||||
for (DataAccessWindow daw : daws) {
|
||||
deviceSerialNumbers.add(daw.getDeviceSerialNumber());
|
||||
}
|
||||
Iterable<Fix> windData = connection.getResourceData(start, end, deviceSerialNumbers, Type.gps_latlong, Type.AWA, Type.AWS, Type.HDG);
|
||||
assertFalse(Util.isEmpty(windData));
|
||||
boolean foundWind = false;
|
||||
for (Fix fix : windData) {
|
||||
foundWind = foundWind || fix instanceof AWA || fix instanceof AWS;
|
||||
assertTrue(fix.getTimePoint().compareTo(start) >= 0 && fix.getTimePoint().compareTo(end) <= 0);
|
||||
}
|
||||
assertTrue(foundWind);
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
|
||||
/**
|
||||
* Igtimi fixes come as isolated single fixes from separate sensors, and even if they are attached to the same device,
|
||||
* their time stamps may not be synchronized. Therefore, separate {@link Track} tracks will be used to hold the data
|
||||
* coming from the different sensors attached to the same device, and several of them need to be joined to allow for the
|
||||
* construction, e.g., of a single {@link Wind} of {@link GPSFixMoving} fix.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class IgtimiFixTrackTest extends AbstractTestWithIgtimiConnection {
|
||||
@Test
|
||||
public void testFetchFixesIntoTracks() throws ParseException, ClientProtocolException, IllegalStateException, IOException, org.json.simple.parser.ParseException {
|
||||
setUp();
|
||||
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
|
||||
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:00:00Z"));
|
||||
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:10:00Z"));
|
||||
// URL is https://www.igtimi.com/api/v1/devices/data_access_windows?type=read&start_time=1383811200000&end_time=1383933600000&access_token=3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9
|
||||
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
|
||||
Set<String> deviceSerialNumbers = new HashSet<>();
|
||||
for (DataAccessWindow daw : daws) {
|
||||
deviceSerialNumbers.add(daw.getDeviceSerialNumber());
|
||||
}
|
||||
Map<String, Map<Type, DynamicTrack<Fix>>> data = connection.getResourceDataAsTracks(start, end, deviceSerialNumbers, Type.gps_latlong, Type.AWA, Type.AWS, Type.HDG, Type.HDGM);
|
||||
assertFalse(data.isEmpty());
|
||||
Map<Type, DynamicTrack<Fix>> windSensorMap = data.get("DD-EE-AAHG");
|
||||
assertNotNull(windSensorMap);
|
||||
assertTrue(windSensorMap.containsKey(Type.AWA));
|
||||
assertTrue(windSensorMap.containsKey(Type.AWS));
|
||||
assertTrue(windSensorMap.containsKey(Type.HDGM));
|
||||
final DynamicTrack<Fix> awaTrack = windSensorMap.get(Type.AWA);
|
||||
assertFalse(isEmpty(awaTrack));
|
||||
final DynamicTrack<Fix> awsTrack = windSensorMap.get(Type.AWS);
|
||||
assertFalse(isEmpty(awsTrack));
|
||||
final DynamicTrack<Fix> hdgmTrack = windSensorMap.get(Type.HDGM);
|
||||
assertFalse(isEmpty(hdgmTrack));
|
||||
}
|
||||
|
||||
private boolean isEmpty(final DynamicTrack<Fix> track) {
|
||||
track.lockForRead();
|
||||
try {
|
||||
return Util.isEmpty(track.getRawFixes());
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-4
@@ -28,9 +28,14 @@ import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Client;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.LiveDataConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.ClientImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectionFactoryImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory;
|
||||
@@ -142,6 +147,7 @@ public class WebSocketTest {
|
||||
|
||||
@Test
|
||||
public void testWebSocketConnect() throws Exception {
|
||||
final List<Fix> allFixesReceived = new ArrayList<>();
|
||||
final Client client = new ClientImpl("7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6",
|
||||
"aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback");
|
||||
MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration();
|
||||
@@ -149,9 +155,16 @@ public class WebSocketTest {
|
||||
final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(client, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService),
|
||||
PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoTestService));
|
||||
Account account = igtimiConnectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||
WebSocketConnectionManager manager = new WebSocketConnectionManager(igtimiConnectionFactory, Collections.singleton("GA-EN-AAEJ"), account);
|
||||
assertNotNull(manager);
|
||||
assertTrue("Connection handshake not successful within 5s", manager.waitForConnection(5000));
|
||||
manager.disconnect();
|
||||
IgtimiConnection conn = igtimiConnectionFactory.connect(account);
|
||||
LiveDataConnection liveDataConnection = conn.createLiveConnection(Collections.singleton("GA-EN-AAEJ"));
|
||||
liveDataConnection.addListener(new BulkFixReceiver() {
|
||||
@Override
|
||||
public void received(Iterable<Fix> fixes) {
|
||||
Util.addAll(fixes, allFixesReceived);
|
||||
}
|
||||
});
|
||||
assertNotNull(liveDataConnection);
|
||||
assertTrue("Connection handshake not successful within 5s", liveDataConnection.waitForConnection(5000l));
|
||||
liveDataConnection.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ Require-Bundle: org.json.simple;bundle-version="1.1.0",
|
||||
org.apache.httpcomponents.httpcore;bundle-version="4.0.0",
|
||||
org.apache.commons.logging;bundle-version="1.1.1",
|
||||
com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.igtimiadapter.persistence
|
||||
com.sap.sailing.domain.igtimiadapter.persistence,
|
||||
com.sap.sailing.domain.shared.android,
|
||||
com.sap.sailing.domain,
|
||||
com.sap.sailing.declination
|
||||
Import-Package: javax.ws.rs;version="1.1.1",
|
||||
javax.ws.rs.core;version="1.1.1",
|
||||
javax.ws.rs.ext;version="1.1.1",
|
||||
@@ -27,3 +30,5 @@ Import-Package: javax.ws.rs;version="1.1.1",
|
||||
org.osgi.framework;version="1.7.0"
|
||||
Web-ContextPath: /igtimi
|
||||
Bundle-Activator: com.sap.sailing.domain.igtimiadapter.impl.Activator
|
||||
Export-Package: com.sap.sailing.domain.igtimiadapter,
|
||||
com.sap.sailing.domain.igtimiadapter.datatypes
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
|
||||
/**
|
||||
* Allows listeners to receive a batch of fixes that the connector received in one transaction. This will usually
|
||||
* improve chances that sensor readings belonging together (such as an AWA and an AWS fix) will be contained in the
|
||||
* same batch.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface BulkFixReceiver {
|
||||
void received(Iterable<Fix> fixes);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
/**
|
||||
* When a device is shared to you, all that happens is that a DAW (Data Access Window) is being created for the time
|
||||
* interval that the device owner wants you to have access to the data from the device.<p>
|
||||
*
|
||||
* So you’re not actually sharing a device per-se, rather you are granting access to data that it creates. This means
|
||||
* that you don’t see shared devices in the devices::list call – that’s only for devices that you own at the instant in
|
||||
* time the call is made. DAW’s allow granting access to data from a device for any interval in time; past, present or
|
||||
* future.<p>
|
||||
*
|
||||
* So if you need to get a complete list of serials that you can request data for, whether or not you are the owner,
|
||||
* then you need to use the data_access_windows:: call.<p>
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface DataAccessWindow extends HasStartAndEndTime, HasPermissions {
|
||||
String getDeviceSerialNumber();
|
||||
|
||||
SecurityEntity getRecipient();
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
public interface Device extends HasId {
|
||||
/**
|
||||
* @return a string identifying the device, such as "AA-AA-AAAA"
|
||||
*/
|
||||
String getSerialNumber();
|
||||
|
||||
String getServiceTag();
|
||||
|
||||
Long getOwnerId();
|
||||
|
||||
Long getDeviceUserGroupId();
|
||||
|
||||
Long getAdminDeviceUserGroupId();
|
||||
|
||||
Iterable<Permission> getPermissions();
|
||||
|
||||
Boolean getBlob();
|
||||
|
||||
String getName();
|
||||
|
||||
User getOwner() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
}
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
|
||||
/**
|
||||
* Notifies a {@link IgtimiFixReceiver receiver} about each fix received.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class FixVisitor implements BulkFixReceiver {
|
||||
private final IgtimiFixReceiver receiver;
|
||||
|
||||
public FixVisitor(IgtimiFixReceiver receiver) {
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(Iterable<Fix> fixes) {
|
||||
for (Fix fix : fixes) {
|
||||
fix.notify(receiver);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface Group extends SecurityEntity {
|
||||
String getName();
|
||||
|
||||
Boolean isHidden();
|
||||
|
||||
Iterable<Permission> getPermissions();
|
||||
|
||||
Boolean isBlob();
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface HasId {
|
||||
long getId();
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface HasPermissions {
|
||||
Iterable<Permission> getPermissions();
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
|
||||
public interface HasStartAndEndTime {
|
||||
TimePoint getEndTime();
|
||||
|
||||
TimePoint getStartTime();
|
||||
}
|
||||
+101
-1
@@ -9,6 +9,8 @@ import org.json.simple.parser.ParseException;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
|
||||
/**
|
||||
* A connection to the Igtimi system for one {@link Client} and one {@link Account}.
|
||||
@@ -19,6 +21,8 @@ public interface IgtimiConnection {
|
||||
|
||||
Iterable<User> getUsers() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
User getUser(long id) throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
/**
|
||||
* @param startTime
|
||||
* optional; may be <code>null</code>
|
||||
@@ -36,9 +40,105 @@ public interface IgtimiConnection {
|
||||
|
||||
/**
|
||||
* All arguments are mandatory.
|
||||
*
|
||||
* @param deviceSerialNumbers
|
||||
* the serial numbers of the devices for which to return data; these numbers can be obtained, e.g., from
|
||||
* {@link #getOwnedDevices()}.{@link Device#getSerialNumber() getSerialNumber()} or from
|
||||
* {@link #getDataAccessWindows(Permission, TimePoint, TimePoint, Iterable)}.
|
||||
* {@link DataAccessWindow#getDeviceSerialNumber() getDeviceSerialNumber()}.
|
||||
* @param typeAndCompression
|
||||
* for each data type to be obtained, tells the compression level; <code>0.0</code> is a good default,
|
||||
* meaning "no compression". Compression is currently only supported for type {@link Type#gps_latlong} where
|
||||
* the number provided represents a maximum error in degrees of latitude and longitude.
|
||||
*/
|
||||
Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
|
||||
Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime, Iterable<String> deviceSerialNumbers,
|
||||
Map<Type, Double> typeAndCompression) throws IllegalStateException, ClientProtocolException, IOException,
|
||||
ParseException;
|
||||
|
||||
/**
|
||||
* Shorthand for {@link #getResourceData(TimePoint, TimePoint, Iterable, Map)} where no compression is requested for
|
||||
* any type.
|
||||
*/
|
||||
Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime, Iterable<String> deviceSerialNumbers,
|
||||
Type... types) throws IllegalStateException, ClientProtocolException, IOException,
|
||||
ParseException;
|
||||
|
||||
/**
|
||||
* Shorthand for {@link #getResourceData(TimePoint, TimePoint, Iterable, Map)} where no compression is requested for
|
||||
* any type. The fixes received are forwarded to the {@link BulkFixReceiver} <code>bulkFixReceiver</code> in one call.
|
||||
*/
|
||||
Iterable<Fix> getAndNotifyResourceData(TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceSerialNumbers, BulkFixReceiver bulkFixReceiver, Type... types)
|
||||
throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
|
||||
/**
|
||||
* Same as {@link #getResourceData(TimePoint, TimePoint, Iterable, Type...)}, but the resulting {@link Fix}es are
|
||||
* grouped into {@link Track}s per fix type and per device.
|
||||
*
|
||||
* @return a map whose keys are the devices' serial numbers and whose values are the fixes produced by the device
|
||||
* identified by the key, grouped in a map with the fix {@link Type} as its key. Note that if a device
|
||||
* didn't produce any fixes at all under the requested parameters, its serial number may not appear as a key
|
||||
* in the map. Note further that should a device not have produced fixes of a given type, that type won't
|
||||
* appear as a key in the map for that device.
|
||||
*/
|
||||
Map<String, Map<Type, DynamicTrack<Fix>>> getResourceDataAsTracks(TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceSerialNumbers, Type... types) throws IllegalStateException, ClientProtocolException,
|
||||
IOException, ParseException;
|
||||
|
||||
/**
|
||||
* For the devices specified by <code>deviceSerialNumbers</code>, creates a live data connection. The <code>account</code>
|
||||
* needs to be authorized to access the devices' data for the current time window. Fixes received through this connection
|
||||
* are forwarded in the batches in which they are received to the listeners that can be added to the live connection
|
||||
* using {@link LiveDataConnection#addListener(BulkFixReceiver)}.
|
||||
*
|
||||
* @return a connection that the caller can use to stop the live feed by calling {@link LiveDataConnection#disconnect()}.
|
||||
*/
|
||||
LiveDataConnection createLiveConnection(Iterable<String> deviceSerialNumbers) throws Exception;
|
||||
|
||||
/**
|
||||
* @param sessionIds
|
||||
* the optional IDs of the sessions for which to obtain the metadata; if <code>null</code>, all available
|
||||
* sessions will be returned
|
||||
* @param isPublic
|
||||
* optional; if <code>null</code> or <code>true</code>, only public sessions will be included in the
|
||||
* result
|
||||
* @param limit
|
||||
* optional; if not <code>null</code>, no more than this many session objects will be returned
|
||||
* @param includeIncomplete
|
||||
* optional; if not <code>false</code>, only completed sessions that have a start and an end time will be
|
||||
* returned
|
||||
*/
|
||||
Iterable<Session> getSessions(Iterable<Long> sessionIds, Boolean isPublic, Integer limit, Boolean includeIncomplete)
|
||||
throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
Session getSession(long id) throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Returns the devices owned by the user to which the application client represented by this connection belongs.
|
||||
*/
|
||||
Iterable<Device> getOwnedDevices() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
/**
|
||||
* Returns all devices that this connection has access to with the requested <code>permission</code>. Note that
|
||||
* these don't necessarily need to be devices owned by the user to which this connection belongs. The user only
|
||||
* needs to have been authorized by the owner of the data to access the respective window of data.
|
||||
*
|
||||
* @param startTime
|
||||
* optional; may be <code>null</code>. If provided, only data access windows whose time frame has a
|
||||
* non-empty range after this time will be returned.
|
||||
* @param endTime
|
||||
* optional; may be <code>null</code>. If provided, only data access windows whose time frame has a
|
||||
* non-empty range before this time will be returned.
|
||||
* @param deviceSerialNumbers
|
||||
* optional; if not <code>null</code> and not empty, only data access windows for the devices identified
|
||||
* by these serial numbers will be returned
|
||||
*/
|
||||
Iterable<DataAccessWindow> getDataAccessWindows(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceSerialNumbers) throws IllegalStateException, ClientProtocolException, IOException,
|
||||
ParseException;
|
||||
|
||||
Iterable<Group> getGroups() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
|
||||
Account getAccount();
|
||||
}
|
||||
+12
-55
@@ -1,21 +1,13 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.igtimiadapter.oauth.AuthorizationCallback;
|
||||
|
||||
/**
|
||||
@@ -26,6 +18,10 @@ import com.sap.sailing.domain.igtimiadapter.oauth.AuthorizationCallback;
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public interface IgtimiConnectionFactory {
|
||||
Iterable<Account> getAllAccounts();
|
||||
|
||||
IgtimiConnection connect(Account account);
|
||||
|
||||
/**
|
||||
* Obtains a URL that a user agent (e.g., a web browser) can be sent to in order to allow that user to authenticate
|
||||
* and then authorize this factory's {@link Client} for accessing the user's Igtimi data. The URL is chosen such that
|
||||
@@ -47,56 +43,17 @@ public interface IgtimiConnectionFactory {
|
||||
* Matches <code>eMail</code> with the e-mail information retrieved from the "account" service earlier when an access
|
||||
* token was registered.
|
||||
*/
|
||||
Account getAccountByEmail(String eMail);
|
||||
|
||||
IgtimiConnection connect(Account account);
|
||||
Account getExistingAccountByEmail(String eMail);
|
||||
|
||||
/**
|
||||
* Uses the /oauth/token service to obtain and {@link #registerAccountForWhichClientIsAuthorized(String) register}
|
||||
* an access token for an authorization code which encodes the authorization given by a user to this factory's
|
||||
* client.
|
||||
* Tries to authorize our client on behalf of a user identified by e-mail and password. If successful, the
|
||||
* account data and the relevant OAuth access token will be stored persistently.
|
||||
*
|
||||
* @return the account encoding the application that is authorized for a user's account
|
||||
* @throws RuntimeException in case there was an error while retrieving the token
|
||||
* @return the account with which a caller can then {@link #connect obtain a connection} for the data that the user
|
||||
* identified by <code>userEmail</code> and <code>userPassword</code> shares with our client.
|
||||
*/
|
||||
Account obtainAccessTokenFromAuthorizationCode(String code) throws UnsupportedEncodingException,
|
||||
ClientProtocolException, IOException, IllegalStateException, ParseException;
|
||||
|
||||
String getAccountUrl(Account account);
|
||||
|
||||
String getUsersUrl(Account account);
|
||||
|
||||
String getResourcesUrl(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Iterable<String> streamIds, Account account);
|
||||
|
||||
String getResourceDataUrl(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
|
||||
Map<Type, Double> typeAndCompression, Account account);
|
||||
|
||||
Iterable<URI> getWebsocketServers() throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException;
|
||||
|
||||
HttpClient getHttpClient();
|
||||
|
||||
/**
|
||||
* Retrieves the JSON object to send in its string-serialized form to a web socket connection in order to receive
|
||||
* live data from the units whose IDs are specified by <code>unitIds</code>. The sending units are expected to
|
||||
* belong to the user account to which this factory's application client has been granted permission.
|
||||
*
|
||||
* @param account
|
||||
* represents this factory's client's permissions to access a user account's data
|
||||
* @param deviceIds
|
||||
* IDs of the transmitting units expected to be visible to the account's {@link Account#getUser() user's}
|
||||
*/
|
||||
JSONObject getWebSocketConfigurationMessage(Account account, Iterable<String> deviceIds);
|
||||
|
||||
Iterable<Account> getAllAccounts();
|
||||
|
||||
/**
|
||||
* Tries to authorize our client on behalf of a user identified by e-mail and password.
|
||||
*
|
||||
* @return the authorization code which can then be used to obtain a permanent access token to be used by our client
|
||||
* to access data owned by the user identified by e-mail and password.
|
||||
*/
|
||||
String authorizeAndReturnAuthorizedCode(String userEmail, String userPassword) throws ClientProtocolException,
|
||||
Account createAccountToAccessUserData(String userEmail, String userPassword) throws ClientProtocolException,
|
||||
IOException, IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException,
|
||||
InstantiationException, IllegalAccessException, ClassCastException;
|
||||
InstantiationException, IllegalAccessException, ClassCastException, ParseException;
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AntCbst;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AntHrm;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.BatteryLevel;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.COG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.File;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsAltitude;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualityHdop;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualityIndicator;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualitySatCount;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.SOG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.STW;
|
||||
|
||||
/**
|
||||
* Callback interface for Igtimi fix receivers.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface IgtimiFixReceiver {
|
||||
void received(AntCbst fix);
|
||||
void received(AntHrm fix);
|
||||
void received(AWA fix);
|
||||
void received(AWS fix);
|
||||
void received(BatteryLevel fix);
|
||||
void received(COG fix);
|
||||
void received(File fix);
|
||||
void received(GpsAltitude fix);
|
||||
void received(GpsLatLong fix);
|
||||
void received(GpsQualityHdop fix);
|
||||
void received(GpsQualityIndicator fix);
|
||||
void received(GpsQualitySatCount fix);
|
||||
void received(HDG fix);
|
||||
void received(HDGM fix);
|
||||
void received(SOG fix);
|
||||
void received(STW fix);
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AntCbst;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AntHrm;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.BatteryLevel;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.COG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.File;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsAltitude;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualityHdop;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualityIndicator;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsQualitySatCount;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.SOG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.STW;
|
||||
|
||||
public class IgtimiFixReceiverAdapter implements IgtimiFixReceiver {
|
||||
@Override
|
||||
public void received(AntCbst fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(AntHrm fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(AWA fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(AWS fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(BatteryLevel fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(COG fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(File fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(GpsAltitude fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(GpsLatLong fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(GpsQualityHdop fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(GpsQualityIndicator fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(GpsQualitySatCount fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(HDG fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(HDGM fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(SOG fix) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(STW fix) {
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface LiveDataConnection {
|
||||
/**
|
||||
* Disconnects this connection. Afterwards, the connection will no longer feed live data to its listener(s).
|
||||
*/
|
||||
void disconnect() throws Exception;
|
||||
|
||||
/**
|
||||
* Waits for a successful connection for <code>timeoutInMillis</code> milliseconds. If the connection is successful
|
||||
* within this time, <code>true</code> is returned; <code>false</code> otherwise.
|
||||
*
|
||||
* @param timeoutInMillis
|
||||
* use 0 to wait indefinitely
|
||||
*/
|
||||
boolean waitForConnection(long timeoutInMillis) throws InterruptedException;
|
||||
|
||||
void addListener(BulkFixReceiver listener);
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
|
||||
public interface LiveDataListener {
|
||||
void fixesReceived(Iterable<Fix> fixes);
|
||||
}
|
||||
+4
-13
@@ -1,21 +1,12 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
|
||||
public interface Resource {
|
||||
public interface Resource extends HasId, HasPermissions, HasStartAndEndTime {
|
||||
|
||||
long getId();
|
||||
boolean isBlob();
|
||||
|
||||
public abstract boolean isBlob();
|
||||
int[] getDataTypes();
|
||||
|
||||
public abstract Iterable<Permission> getPermissions();
|
||||
|
||||
public abstract int[] getDataTypes();
|
||||
|
||||
public abstract String getDeviceSerialNumber();
|
||||
|
||||
public abstract TimePoint getEndTime();
|
||||
|
||||
public abstract TimePoint getStartTime();
|
||||
String getDeviceSerialNumber();
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
/**
|
||||
* Some entity to be authorized to access some data, such as a user or a group.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface SecurityEntity extends HasId {
|
||||
|
||||
}
|
||||
+9
-4
@@ -2,11 +2,16 @@ package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface Sensor {
|
||||
/**
|
||||
* The ID called "sensor ID" by Igtimi. This identifies the microcontroller-like device that can mange several sensors
|
||||
* such as wind, GPS or heart rate. Together with the type code and the optional {@link #getDeviceId() device ID} as used
|
||||
* The ID called "serial number" by Igtimi. This identifies the microcontroller-like device that can mange several sensors
|
||||
* such as wind, GPS or heart rate. Together with the type code and the optional {@link #getSensorId() sub-device ID} as used
|
||||
* for heart rate sensors the individual sensor is uniquely defined.
|
||||
*/
|
||||
String getTransmitterId();
|
||||
String getDeviceSerialNumber();
|
||||
|
||||
String getDeviceId();
|
||||
/**
|
||||
* By default, the sensor ID is 0, particularly if it represents a sensor built into the device, such as a built-in GPS.
|
||||
* In case of multiple sensors of the same kind, the non-zero ID will then help to keep the individual sensors attached
|
||||
* to the same device apart. Example: multiple heart rate sensors.
|
||||
*/
|
||||
long getSensorId();
|
||||
}
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
/**
|
||||
* Sessions are a container that gives meaning and structure to a group of {@link Device devices} that are brought
|
||||
* together at a particular time for a race etc. It has a few direct properties such as {@link #getName() name},
|
||||
* start+end time. Then it has the session logs which hold all the detail about what’s in the session, such as yachts,
|
||||
* buoys, devices, names and colors. Sessions are shared by adding a {@link User user} into the session's {@link Group}
|
||||
* identified by the {@link #getSessionGroupId() session group id}.
|
||||
* <p>
|
||||
*
|
||||
* Sessions handle all the magic to ensure that {@link #getPermissions() permissions} to access device data (
|
||||
* {@link DataAccessWindow)s} are handled when group membership changes, and as devices are added or removed to the
|
||||
* session via the logs. So once you are familiar with using them it takes away all the load of managing users,
|
||||
* permissions, and access to data. Without sessions, to give you access to an event's data would have meant manually
|
||||
* making a list of devices used in the session and creating a bunch of {@link DataAccessWindow}s for them.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface Session extends HasId, HasPermissions, HasStartAndEndTime {
|
||||
boolean isBlob();
|
||||
|
||||
long getAdminSessionGroupId();
|
||||
|
||||
long getSessionGroupId();
|
||||
|
||||
long getOwnerId();
|
||||
|
||||
String getName();
|
||||
|
||||
User getOwner() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
|
||||
}
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
package com.sap.sailing.domain.igtimiadapter;
|
||||
|
||||
public interface User {
|
||||
long getId();
|
||||
|
||||
public interface User extends SecurityEntity {
|
||||
String getFirstName();
|
||||
|
||||
String getSurname();
|
||||
|
||||
+9
-1
@@ -5,15 +5,18 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* Apparent wind angle, relative to the device's or vessel's orientation
|
||||
* Apparent wind angle, relative to the device's or vessel's orientation, in degrees from 0 to 360, with values 180-360
|
||||
* meaning the port side, and values 0-180 meaning starboard side.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class AWA extends Fix {
|
||||
private static final long serialVersionUID = -5979975741914782196L;
|
||||
private final Bearing apparentWindAngle;
|
||||
|
||||
public AWA(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -29,4 +32,9 @@ public class AWA extends Fix {
|
||||
protected String localToString() {
|
||||
return "AWA: "+getApparentWindAngle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,7 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class AWS extends Fix {
|
||||
private static final long serialVersionUID = -4044229400122127582L;
|
||||
private final Speed apparentWindSpeed;
|
||||
|
||||
public AWS(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -29,4 +31,9 @@ public class AWS extends Fix {
|
||||
protected String localToString() {
|
||||
return "AWS: "+getApparentWindSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,7 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class AntCbst extends Fix {
|
||||
private static final long serialVersionUID = 7696345613317022955L;
|
||||
private final int cadence;
|
||||
|
||||
public AntCbst(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -27,4 +29,9 @@ public class AntCbst extends Fix {
|
||||
public String localToString() {
|
||||
return "Cadence "+getCadence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+20
-1
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -12,11 +13,24 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class AntHrm extends Fix {
|
||||
private static final long serialVersionUID = 94260324289901500L;
|
||||
private final int eventTime;
|
||||
private final int heartBeatCount;
|
||||
private final int heartRate;
|
||||
|
||||
public AntHrm(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
heartRate = ((Number) valuesPerSubindex.get(1)).intValue();
|
||||
eventTime = ((Number) valuesPerSubindex.get(1)).intValue();
|
||||
heartBeatCount = ((Number) valuesPerSubindex.get(2)).intValue();
|
||||
heartRate = ((Number) valuesPerSubindex.get(3)).intValue();
|
||||
}
|
||||
|
||||
public int getEventTime() {
|
||||
return eventTime;
|
||||
}
|
||||
|
||||
public int getHeartBeatCount() {
|
||||
return heartBeatCount;
|
||||
}
|
||||
|
||||
public int getHeartRate() {
|
||||
@@ -27,4 +41,9 @@ public class AntHrm extends Fix {
|
||||
protected String localToString() {
|
||||
return "heart rate "+getHeartRate()+"/min";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -3,9 +3,11 @@ package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class BatteryLevel extends Fix {
|
||||
private static final long serialVersionUID = -8499201867081319088L;
|
||||
private final double percentage;
|
||||
|
||||
public BatteryLevel(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -21,4 +23,9 @@ public class BatteryLevel extends Fix {
|
||||
protected String localToString() {
|
||||
return "Battery: "+getPercentage()+"%";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,7 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class COG extends Fix {
|
||||
private static final long serialVersionUID = 1105936972187917331L;
|
||||
private final Bearing courseOverGround;
|
||||
|
||||
public COG(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -29,4 +31,9 @@ public class COG extends Fix {
|
||||
protected String localToString() {
|
||||
return "COG: "+getCourseOverGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -3,9 +3,11 @@ package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class File extends Fix {
|
||||
private static final long serialVersionUID = 7580174784400926003L;
|
||||
private final TimePoint start;
|
||||
private final TimePoint end;
|
||||
private final String fileName;
|
||||
@@ -51,4 +53,9 @@ public class File extends Fix {
|
||||
protected String localToString() {
|
||||
return "File: "+getFileName()+", MD5: "+getMd5()+" content type "+getContentType()+", "+getSize()+" bytes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -1,9 +1,12 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
|
||||
import com.sap.sailing.domain.base.Timed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public abstract class Fix {
|
||||
public abstract class Fix implements Timed {
|
||||
private static final long serialVersionUID = -486253194440558670L;
|
||||
private final TimePoint timePoint;
|
||||
private final Sensor sensor;
|
||||
|
||||
@@ -12,7 +15,12 @@ public abstract class Fix {
|
||||
this.timePoint = timePoint;
|
||||
this.sensor = sensor;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return Type.getType(this.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getTimePoint() {
|
||||
return timePoint;
|
||||
}
|
||||
@@ -23,6 +31,8 @@ public abstract class Fix {
|
||||
|
||||
abstract protected String localToString();
|
||||
|
||||
abstract public void notify(IgtimiFixReceiver receiver);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return localToString() + " at "+getTimePoint()+" from "+getSensor();
|
||||
|
||||
+7
@@ -5,9 +5,11 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsAltitude extends Fix {
|
||||
private static final long serialVersionUID = -5740764665236002412L;
|
||||
private final Distance altitude;
|
||||
|
||||
public GpsAltitude(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -23,4 +25,9 @@ public class GpsAltitude extends Fix {
|
||||
protected String localToString() {
|
||||
return "Altitude "+getAltitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -5,9 +5,11 @@ import java.util.Map;
|
||||
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.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsLatLong extends Fix {
|
||||
private static final long serialVersionUID = 5056284867725893553L;
|
||||
private final Position position;
|
||||
|
||||
public GpsLatLong(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -23,4 +25,9 @@ public class GpsLatLong extends Fix {
|
||||
protected String localToString() {
|
||||
return position.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -5,9 +5,11 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsQualityHdop extends Fix {
|
||||
private static final long serialVersionUID = -5319766127405258823L;
|
||||
private final Distance hdop;
|
||||
|
||||
public GpsQualityHdop(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -23,4 +25,9 @@ public class GpsQualityHdop extends Fix {
|
||||
protected String localToString() {
|
||||
return "HDOP: "+getHdop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -3,9 +3,11 @@ package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class GpsQualityIndicator extends Fix {
|
||||
private static final long serialVersionUID = 1112428061064399194L;
|
||||
private final int quality;
|
||||
|
||||
public GpsQualityIndicator(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -21,4 +23,9 @@ public class GpsQualityIndicator extends Fix {
|
||||
protected String localToString() {
|
||||
return "GPS Quality: "+getQuality();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+28
@@ -3,9 +3,32 @@ package com.sap.sailing.domain.igtimiadapter.datatypes;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
* For YachtBot and WindBot devices, this lists the number of satellites the device currently uses to determine its
|
||||
* position. For the YachtBot smartphone app, restrictions may apply, as per the following e-mail by Brent Russell,
|
||||
* Igtimi CTO, sent at 2013-12-01T21:16:00Z:
|
||||
*
|
||||
* <pre>
|
||||
* You're correct, we're not reading or propagating it at the moment from mobile devices, although it works fine with YachtBot hardware.
|
||||
* The mobile platform is what we're giving away for free, so we're not trying to make it a direct feature match for YachtBot :)
|
||||
*
|
||||
* Both mobile and YachtBot only Tx GPS coordinates when there is a valid fix, so in practice I think you should never see a zeroed
|
||||
* SatQ or SatC that is of any true meaning anyway. Best just to ignore both these types (or throw away if zero) and not request it
|
||||
* with the ::data call.
|
||||
*
|
||||
* We do have some plans to change the way fix quality information is transmitted. Mostly it's so that changes only are transmitted,
|
||||
* and we'll encourage people to use hdop or gdop rather than satellite count. There's some changes we need to make under the hood
|
||||
* first though, so not a near future thing.
|
||||
* </pre>
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class GpsQualitySatCount extends Fix {
|
||||
private static final long serialVersionUID = -5507100027164944068L;
|
||||
private final int satCount;
|
||||
|
||||
public GpsQualitySatCount(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -21,4 +44,9 @@ public class GpsQualitySatCount extends Fix {
|
||||
protected String localToString() {
|
||||
return ""+getSatCount()+" satellites";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -14,11 +15,12 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class HDG extends Fix {
|
||||
private static final long serialVersionUID = -2400420107580758551L;
|
||||
private final Bearing trueHeading;
|
||||
|
||||
public HDG(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
trueHeading = new DegreeBearingImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
trueHeading = new DegreeBearingImpl((((Number) valuesPerSubindex.get(1)).doubleValue()+360.)%360.);
|
||||
}
|
||||
|
||||
public Bearing getTrueHeading() {
|
||||
@@ -29,4 +31,9 @@ public class HDG extends Fix {
|
||||
protected String localToString() {
|
||||
return "HDG: "+getTrueHeading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -14,11 +15,12 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class HDGM extends Fix {
|
||||
private static final long serialVersionUID = -3344091983385126284L;
|
||||
private final Bearing magnetigHeading;
|
||||
|
||||
public HDGM(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
super(sensor, timePoint);
|
||||
magnetigHeading = new DegreeBearingImpl(((Number) valuesPerSubindex.get(1)).doubleValue());
|
||||
magnetigHeading = new DegreeBearingImpl((((Number) valuesPerSubindex.get(1)).doubleValue()+360.)%360.);
|
||||
}
|
||||
|
||||
public Bearing getMagnetigHeading() {
|
||||
@@ -29,4 +31,9 @@ public class HDGM extends Fix {
|
||||
protected String localToString() {
|
||||
return "HDGM: "+getMagnetigHeading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,7 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class SOG extends Fix {
|
||||
private static final long serialVersionUID = 6926121760778539823L;
|
||||
private final Speed speedOverGround;
|
||||
|
||||
public SOG(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -29,4 +31,9 @@ public class SOG extends Fix {
|
||||
protected String localToString() {
|
||||
return "SOG: "+getSpeedOverGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+7
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,7 @@ import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
*
|
||||
*/
|
||||
public class STW extends Fix {
|
||||
private static final long serialVersionUID = -7854740389524286036L;
|
||||
private final Speed speedThroughWater;
|
||||
|
||||
public STW(TimePoint timePoint, Sensor sensor, Map<Integer, Object> valuesPerSubindex) {
|
||||
@@ -29,4 +31,9 @@ public class STW extends Fix {
|
||||
protected String localToString() {
|
||||
return "STW: "+getSpeedThroughWater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(IgtimiFixReceiver receiver) {
|
||||
receiver.received(this);
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -4,7 +4,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum Type {
|
||||
|
||||
gps_latlong(1, GpsLatLong.class),
|
||||
gps_quality_indicator(2, GpsQualityIndicator.class),
|
||||
gps_quality_sat_count(3, GpsQualitySatCount.class),
|
||||
@@ -37,15 +36,21 @@ public enum Type {
|
||||
private final int code;
|
||||
private final Class<? extends Fix> fixClass;
|
||||
private final static Map<Integer, Type> typeByCode = new HashMap<>();
|
||||
private final static Map<Class<? extends Fix>, Type> typeByClass = new HashMap<>();
|
||||
|
||||
private Type(int code, Class<? extends Fix> fixClass) {
|
||||
this.code = code;
|
||||
this.fixClass = fixClass;
|
||||
}
|
||||
|
||||
public static Type getType(Class<? extends Fix> fixClass) {
|
||||
return typeByClass.get(fixClass);
|
||||
}
|
||||
|
||||
static {
|
||||
for (Type type : Type.values()) {
|
||||
typeByCode.put(type.getCode(), type);
|
||||
typeByClass.put(type.getFixClass(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -33,7 +33,7 @@ public class Activator implements BundleActivator {
|
||||
private static final String CLIENT_ID_PROPERTY_NAME = "igtimi.client.id";
|
||||
private static final String CLIENT_SECRET_PROPERTY_NAME = "igtimi.client.secret";
|
||||
private static final String CLIENT_REDIRECT_URI_PROPERTY_NAME = "igtimi.client.redirecturi";
|
||||
private IgtimiConnectionFactory connectionFactory;
|
||||
private IgtimiConnectionFactoryImpl connectionFactory;
|
||||
|
||||
public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
final String clientId = System.getProperty(CLIENT_ID_PROPERTY_NAME, DEFAULT_CLIENT_ID);
|
||||
@@ -58,7 +58,7 @@ public class Activator implements BundleActivator {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public IgtimiConnectionFactory getConnectionFactory() {
|
||||
public IgtimiConnectionFactoryImpl getConnectionFactory() {
|
||||
return connectionFactory;
|
||||
}
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.SecurityEntity;
|
||||
|
||||
public class DataAccessWindowDeserializer extends HasPermissionsDeserializer {
|
||||
private static final Logger logger = Logger.getLogger(DataAccessWindowDeserializer.class.getName());
|
||||
|
||||
public DataAccessWindow createDataAccessWindowFromJson(JSONObject resourceJson, IgtimiConnection conn) {
|
||||
return new DataAccessWindowImpl((Long) resourceJson.get("id"),
|
||||
new MillisecondsTimePoint(((Double) resourceJson.get("start_time")).longValue()),
|
||||
new MillisecondsTimePoint(((Double) resourceJson.get("end_time")).longValue()),
|
||||
(String) resourceJson.get("device_serial_number"),
|
||||
getPermissions((JSONObject) resourceJson.get("permissions")),
|
||||
getSecurityEntity((JSONObject) resourceJson.get("recipient")));
|
||||
}
|
||||
|
||||
private SecurityEntity getSecurityEntity(JSONObject securityEntity) {
|
||||
if (securityEntity.containsKey("user")) {
|
||||
return new UserDeserializer().createUserFromJson((JSONObject) securityEntity.get("user"));
|
||||
} else if (securityEntity.containsKey("group")) {
|
||||
return new GroupDeserializer().createGroupFromJson((JSONObject) securityEntity.get("group"));
|
||||
} else {
|
||||
logger.warning("Don't know how to deserialize a serucity entity from "+securityEntity);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.SecurityEntity;
|
||||
|
||||
public class DataAccessWindowImpl extends HasIdImpl implements DataAccessWindow {
|
||||
private final TimePoint startTime;
|
||||
private final TimePoint endTime;
|
||||
private final String deviceSerialNumber;
|
||||
private final Iterable<Permission> permissions;
|
||||
private final SecurityEntity recipient;
|
||||
|
||||
public DataAccessWindowImpl(long id, TimePoint startTime, TimePoint endTime, String deviceSerialNumber,
|
||||
Iterable<Permission> permissions, SecurityEntity recipient) {
|
||||
super(id);
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.deviceSerialNumber = deviceSerialNumber;
|
||||
this.permissions = permissions;
|
||||
this.recipient = recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceSerialNumber() {
|
||||
return deviceSerialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityEntity getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DAW "+getId()+" for device "+getDeviceSerialNumber()+" from "+getStartTime()+" to "+getEndTime()+", permissions "+getPermissions();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Device;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
|
||||
public class DeviceDeserializer extends HasPermissionsDeserializer {
|
||||
public Device createResourceFromJson(JSONObject sessionJson, IgtimiConnection conn) {
|
||||
Boolean blob = (Boolean) sessionJson.get("blob");
|
||||
return new DeviceImpl((Long) sessionJson.get("id"), (String) sessionJson.get("serial_number"), (String) sessionJson.get("name"),
|
||||
(String) sessionJson.get("service_tag"), (Long) sessionJson.get("owner_id"),
|
||||
(Long) sessionJson.get("device_user_group_id"), (Long) sessionJson.get("admin_device_user_group_id"),
|
||||
getPermissions((JSONObject) sessionJson.get("permissions")),
|
||||
blob == null ? false : blob, conn);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Device;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
|
||||
public class DeviceImpl extends HasIdImpl implements Device {
|
||||
private final String serialNumber;
|
||||
private final String name;
|
||||
private String serviceTag;
|
||||
private Long ownerId;
|
||||
private Long deviceUserGroupId;
|
||||
private Long adminDeviceUserGroupId;
|
||||
private Iterable<Permission> permissions;
|
||||
private Boolean blob;
|
||||
private final IgtimiConnection conn;
|
||||
|
||||
protected DeviceImpl(long id, String serialNumber, IgtimiConnection conn) {
|
||||
this(id, serialNumber, /* name */ null, /* serviceTag */ null, /* ownerId */ null, /* deviceUserGroupId */ null,
|
||||
/* adminDeviceUserGroupId */ null, /* permissions */ null, /* blob */ null, conn);
|
||||
}
|
||||
|
||||
public DeviceImpl(Long id, String serialNumber, String name, String serviceTag, Long ownerId, Long deviceUserGroupId,
|
||||
Long adminDeviceUserGroupId, Iterable<Permission> permissions, Boolean blob, IgtimiConnection conn) {
|
||||
super(id);
|
||||
this.serialNumber = serialNumber;
|
||||
this.name = name;
|
||||
this.serviceTag = serialNumber;
|
||||
this.ownerId = ownerId;
|
||||
this.deviceUserGroupId = deviceUserGroupId;
|
||||
this.adminDeviceUserGroupId = adminDeviceUserGroupId;
|
||||
this.permissions = permissions;
|
||||
this.blob = blob;
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceTag() {
|
||||
return serviceTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDeviceUserGroupId() {
|
||||
return deviceUserGroupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAdminDeviceUserGroupId() {
|
||||
return adminDeviceUserGroupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getOwner() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
return conn.getUser(getOwnerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBlob() {
|
||||
return blob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -25,7 +25,7 @@ public class FixFactory {
|
||||
public Iterable<Fix> createFixes(JSONObject sensorsJson) {
|
||||
List<Fix> result = new ArrayList<>();
|
||||
for (Entry<Object, Object> e : sensorsJson.entrySet()) {
|
||||
String sensorId = (String) e.getKey();
|
||||
String deviceSerialNumber = (String) e.getKey();
|
||||
JSONObject typesJson = (JSONObject) e.getValue();
|
||||
for (Entry<Object, Object> fixTypeAndFixesJson : typesJson.entrySet()) {
|
||||
final String[] fixTypeAndOptionalColonSeparatedSensorsSubId = ((String) fixTypeAndFixesJson.getKey()).split(":");
|
||||
@@ -42,7 +42,9 @@ public class FixFactory {
|
||||
valuesPerSubindex.put(i, (Number) values.get(fixIndex));
|
||||
i++;
|
||||
}
|
||||
Sensor sensor = new SensorImpl(sensorId, fixTypeAndOptionalColonSeparatedSensorsSubId.length<2?null:fixTypeAndOptionalColonSeparatedSensorsSubId[1]);
|
||||
Sensor sensor = new SensorImpl(deviceSerialNumber,
|
||||
fixTypeAndOptionalColonSeparatedSensorsSubId.length < 2 ? 0
|
||||
: Long.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[1]));
|
||||
Fix fix = createFix(sensor, Type.valueOf(fixType), timePoint, valuesPerSubindex);
|
||||
result.add(fix);
|
||||
fixIndex++;
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Group;
|
||||
|
||||
public class GroupDeserializer extends HasPermissionsDeserializer {
|
||||
public Group createGroupFromJson(JSONObject groupJson) {
|
||||
Boolean blob = (Boolean) groupJson.get("blob");
|
||||
final Long id = (Long) groupJson.get("id");
|
||||
return new GroupImpl(id,
|
||||
(String) groupJson.get("name"),
|
||||
getPermissions((JSONObject) groupJson.get("permissions")),
|
||||
(Boolean) groupJson.get("hidden"),
|
||||
blob == null ? false : blob);
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Group;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
|
||||
public class GroupImpl extends HasIdImpl implements Group {
|
||||
private final String name;
|
||||
private final Iterable<Permission> permissions;
|
||||
private final Boolean hidden;
|
||||
private final Boolean blob;
|
||||
|
||||
public GroupImpl(long id, String name, Iterable<Permission> permissions, Boolean hidden, Boolean blob) {
|
||||
super(id);
|
||||
this.name = name;
|
||||
this.permissions = permissions;
|
||||
this.hidden = hidden;
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isBlob() {
|
||||
return blob;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.HasId;
|
||||
|
||||
public class HasIdImpl implements HasId {
|
||||
private final long id;
|
||||
|
||||
protected HasIdImpl(long id) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
|
||||
public abstract class HasPermissionsDeserializer {
|
||||
protected Iterable<Permission> getPermissions(JSONObject permissions) {
|
||||
final List<Permission> result;
|
||||
if (permissions == null) {
|
||||
result = null;
|
||||
} else {
|
||||
result = new ArrayList<>();
|
||||
for (Entry<Object, Object> e : permissions.entrySet()) {
|
||||
if ((Boolean) e.getValue()) {
|
||||
result.add(Permission.valueOf((String) e.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+109
-14
@@ -120,7 +120,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account getAccountByEmail(String eMail) {
|
||||
public Account getExistingAccountByEmail(String eMail) {
|
||||
return accountsByEmail.get(eMail);
|
||||
}
|
||||
|
||||
@@ -138,12 +138,20 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
return accessTokensByAccount.get(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccountUrl(Account account) {
|
||||
return getApiV1BaseUrl()+"account?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Retrieves the JSON object to send in its string-serialized form to a web socket connection in order to receive
|
||||
* live data from the units whose IDs are specified by <code>unitIds</code>. The sending units are expected to
|
||||
* belong to the user account to which this factory's application client has been granted permission.
|
||||
*
|
||||
* @param account
|
||||
* represents this factory's client's permissions to access a user account's data
|
||||
* @param deviceIds
|
||||
* IDs of the transmitting units expected to be visible to the account's {@link Account#getUser() user's}
|
||||
*/
|
||||
public JSONObject getWebSocketConfigurationMessage(Account account, Iterable<String> deviceIds) {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("access_token", getAccessTokenForAccount(account));
|
||||
@@ -155,12 +163,54 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsersUrl(Account account) {
|
||||
return getApiV1BaseUrl()+"users?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserUrl(long id, Account account) {
|
||||
return getApiV1BaseUrl()+"users/"+id+"?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
public String getGroupsUrl(Account account) {
|
||||
return getApiV1BaseUrl()+"groups?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
public String getGroupUrl(long id, Account account) {
|
||||
return getApiV1BaseUrl()+"groups/"+id+"?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
public String getSessionUrl(long id, Account account) {
|
||||
return getApiV1BaseUrl()+"sessions/"+id+"?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
public String getOwnedDevicesUrl(Account account) {
|
||||
return getApiV1BaseUrl()+"devices/?"+getAccessTokenUrlParameter(account);
|
||||
}
|
||||
|
||||
public String getDataAccessWindowsUrl(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceSerialNumbers, Account account) {
|
||||
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
|
||||
url.append("devices/data_access_windows?type=");
|
||||
url.append(permission.name());
|
||||
if (startTime != null) {
|
||||
url.append("&start_time=");
|
||||
url.append(startTime.asMillis());
|
||||
}
|
||||
if (endTime != null) {
|
||||
url.append("&end_time=");
|
||||
url.append(endTime.asMillis());
|
||||
}
|
||||
if (deviceSerialNumbers != null) {
|
||||
for (String serialNumber : deviceSerialNumbers) {
|
||||
url.append("&serial_numbers[]=");
|
||||
url.append(serialNumber);
|
||||
}
|
||||
}
|
||||
url.append("&");
|
||||
url.append(getAccessTokenUrlParameter(account));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
public String getResourceDataUrl(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
|
||||
Map<Type, Double> typeAndCompression, Account account) {
|
||||
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
|
||||
@@ -180,7 +230,6 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourcesUrl(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Iterable<String> streamIds, Account account) {
|
||||
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
|
||||
@@ -212,6 +261,32 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
|
||||
public String getSessionsUrl(Iterable<Long> sessionIds, Boolean isPublic, Integer limit, Boolean includeIncomplete, Account account) {
|
||||
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
|
||||
url.append("sessions?");
|
||||
url.append(getAccessTokenUrlParameter(account));
|
||||
if (sessionIds != null) {
|
||||
for (Long sessionId : sessionIds) {
|
||||
url.append("&ids[]=");
|
||||
url.append(sessionId);
|
||||
}
|
||||
}
|
||||
if (isPublic != null) {
|
||||
url.append("&public=");
|
||||
url.append(isPublic.toString().toLowerCase());
|
||||
}
|
||||
if (limit != null) {
|
||||
url.append("&limit=");
|
||||
url.append(limit);
|
||||
}
|
||||
if (includeIncomplete != null) {
|
||||
url.append("&include_incomplete");
|
||||
url.append(includeIncomplete.toString().toLowerCase());
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private String getAccessTokenUrlParameter(Account account) {
|
||||
return "access_token="+getAccessTokenForAccount(account);
|
||||
}
|
||||
@@ -225,7 +300,14 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Uses the /oauth/token service to obtain and {@link #registerAccountForWhichClientIsAuthorized(String) register}
|
||||
* an access token for an authorization code which encodes the authorization given by a user to this factory's
|
||||
* client.
|
||||
*
|
||||
* @return the account encoding the application that is authorized for a user's account
|
||||
* @throws RuntimeException in case there was an error while retrieving the token
|
||||
*/
|
||||
public Account obtainAccessTokenFromAuthorizationCode(String code) throws ClientProtocolException, IOException, IllegalStateException, ParseException {
|
||||
HttpClient client = new SystemDefaultHttpClient();
|
||||
HttpPost post = new HttpPost(getOauthTokenUrl());
|
||||
@@ -248,7 +330,12 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Tries to authorize our client on behalf of a user identified by e-mail and password.
|
||||
*
|
||||
* @return the authorization code which can then be used to obtain a permanent access token to be used by our client
|
||||
* to access data owned by the user identified by e-mail and password.
|
||||
*/
|
||||
public String authorizeAndReturnAuthorizedCode(String userEmail, String userPassword)
|
||||
throws ClientProtocolException, IOException, IllegalStateException, ParserConfigurationException,
|
||||
SAXException, ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
|
||||
@@ -261,6 +348,20 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
HttpResponse responseForAuthorize = client.execute(get);
|
||||
return signInAndReturnAuthorizationForm(client, responseForAuthorize, userEmail, userPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account createAccountToAccessUserData(String userEmail, String userPassword) throws ClientProtocolException,
|
||||
IOException, IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException,
|
||||
InstantiationException, IllegalAccessException, ClassCastException, ParseException {
|
||||
final Account result;
|
||||
String code = authorizeAndReturnAuthorizedCode(userEmail, userPassword);
|
||||
if (code != null) {
|
||||
result = obtainAccessTokenFromAuthorizationCode(code);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String signInAndReturnAuthorizationForm(DefaultHttpClient client, HttpResponse response,
|
||||
final String userEmail, final String userPassword) throws ParserConfigurationException, SAXException,
|
||||
@@ -314,10 +415,6 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
* Parses the form in the <code>autorizationForm</code> response and posts it by submitting the form that contains
|
||||
* the commit button with the value "Authorize". If a redirect strategy is set on the <code>client</code> it will
|
||||
* see the redirect URL in the <code>Location</code> header. The redirection target is closed immediately.
|
||||
* @throws ClassCastException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
private void authorizeAndGetCode(HttpResponse authorizationForm, DefaultHttpClient client)
|
||||
throws IllegalStateException, SAXException, IOException, ParserConfigurationException,
|
||||
@@ -364,13 +461,11 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
authorizationResponse.getEntity().getContent().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
HttpClient client = new SystemDefaultHttpClient();
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<URI> getWebsocketServers() throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException {
|
||||
HttpClient client = getHttpClient();
|
||||
HttpGet getWebsocketServers = new HttpGet("https://www.igtimi.com/server_listers/web_sockets");
|
||||
|
||||
+152
-9
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -14,23 +15,36 @@ import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
|
||||
import com.sap.sailing.domain.igtimiadapter.Device;
|
||||
import com.sap.sailing.domain.igtimiadapter.Group;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.LiveDataConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
import com.sap.sailing.domain.igtimiadapter.Session;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
|
||||
import com.sap.sailing.domain.igtimiadapter.websocket.WebSocketConnectionManager;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackImpl;
|
||||
|
||||
public class IgtimiConnectionImpl implements IgtimiConnection {
|
||||
private final Account account;
|
||||
private final IgtimiConnectionFactory connectionFactory;
|
||||
private final IgtimiConnectionFactoryImpl connectionFactory;
|
||||
|
||||
public IgtimiConnectionImpl(IgtimiConnectionFactory connectionFactory, Account account) {
|
||||
public IgtimiConnectionImpl(IgtimiConnectionFactoryImpl connectionFactory, Account account) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<User> getUsers() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
@@ -44,6 +58,28 @@ public class IgtimiConnectionImpl implements IgtimiConnection {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(long id) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getUsers = new HttpGet(connectionFactory.getUserUrl(id, account));
|
||||
JSONObject userJson = ConnectivityUtils.getJsonFromResponse(client.execute(getUsers));
|
||||
User result = new UserDeserializer().createUserFromJson((JSONObject) ((JSONObject) userJson).get("user"));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Group> getGroups() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getGroups = new HttpGet(connectionFactory.getGroupsUrl(account));
|
||||
JSONObject groupsJson = ConnectivityUtils.getJsonFromResponse(client.execute(getGroups));
|
||||
final List<Group> result = new ArrayList<>();
|
||||
for (Object groupJson : (JSONArray) groupsJson.get("groups")) {
|
||||
Group group = new GroupDeserializer().createGroupFromJson((JSONObject) ((JSONObject) groupJson).get("group"));
|
||||
result.add(group);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Resource> getResources(Permission permission, TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceIds, Iterable<String> streamIds) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
@@ -51,19 +87,126 @@ public class IgtimiConnectionImpl implements IgtimiConnection {
|
||||
HttpGet getResources = new HttpGet(connectionFactory.getResourcesUrl(permission, startTime, endTime, deviceIds, streamIds, account));
|
||||
JSONObject resourcesJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResources));
|
||||
final List<Resource> result = new ArrayList<>();
|
||||
for (Object userJson : (JSONArray) resourcesJson.get("resources")) {
|
||||
Resource user = new ResourceDeserializer().createResourceFromJson((JSONObject) ((JSONObject) userJson).get("resource"));
|
||||
result.add(user);
|
||||
for (Object resourceJson : (JSONArray) resourcesJson.get("resources")) {
|
||||
Resource resource = new ResourceDeserializer().createResourceFromJson((JSONObject) ((JSONObject) resourceJson).get("resource"), this);
|
||||
result.add(resource);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Session> getSessions(Iterable<Long> sessionIds, Boolean isPublic, Integer limit,
|
||||
Boolean includeIncomplete) throws IllegalStateException, ClientProtocolException, IOException,
|
||||
ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getResources = new HttpGet(connectionFactory.getSessionsUrl(sessionIds, isPublic, limit, includeIncomplete, account));
|
||||
JSONObject sessionsJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResources));
|
||||
final List<Session> result = new ArrayList<>();
|
||||
for (Object sessionJson : (JSONArray) sessionsJson.get("sessions")) {
|
||||
Session session = new SessionDeserializer().createResourceFromJson((JSONObject) ((JSONObject) sessionJson).get("session"), this);
|
||||
result.add(session);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> serialNumbers, Map<Type, Double> typeAndCompression) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
public Session getSession(long id) throws IllegalStateException, ClientProtocolException, IOException,
|
||||
ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getResourceData = new HttpGet(connectionFactory.getResourceDataUrl(startTime, endTime, serialNumbers, typeAndCompression, account));
|
||||
HttpGet getSessions = new HttpGet(connectionFactory.getSessionUrl(id, account));
|
||||
JSONObject sessionJson = ConnectivityUtils.getJsonFromResponse(client.execute(getSessions));
|
||||
Session result = new SessionDeserializer().createResourceFromJson((JSONObject) ((JSONObject) sessionJson).get("session"), this);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceSerialNumbers, Map<Type, Double> typeAndCompression) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getResourceData = new HttpGet(connectionFactory.getResourceDataUrl(startTime, endTime, deviceSerialNumbers, typeAndCompression, account));
|
||||
JSONObject resourceDataJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResourceData));
|
||||
return new FixFactory().createFixes(resourceDataJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime, Iterable<String> deviceSerialNumbers,
|
||||
Type... types) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
Map<Type, Double> typeAndCompression = new HashMap<>();
|
||||
for (Type type : types) {
|
||||
typeAndCompression.put(type, 0.0);
|
||||
}
|
||||
return getResourceData(startTime, endTime, deviceSerialNumbers, typeAndCompression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Fix> getAndNotifyResourceData(TimePoint startTime, TimePoint endTime,
|
||||
Iterable<String> deviceSerialNumbers, BulkFixReceiver bulkFixReceiver, Type... types)
|
||||
throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
Iterable<Fix> result = getResourceData(startTime, endTime, deviceSerialNumbers, types);
|
||||
bulkFixReceiver.received(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<Type, DynamicTrack<Fix>>> getResourceDataAsTracks(TimePoint startTime, TimePoint endTime, Iterable<String> deviceSerialNumbers,
|
||||
Type... types) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
Iterable<Fix> fixes = getResourceData(startTime, endTime, deviceSerialNumbers, types);
|
||||
Map<String, Map<Type, DynamicTrack<Fix>>> result = new HashMap<>();
|
||||
for (Fix fix : fixes) {
|
||||
String deviceSerialNumber = fix.getSensor().getDeviceSerialNumber();
|
||||
Type type = fix.getType();
|
||||
DynamicTrack<Fix> track = (DynamicTrack<Fix>) getOrCreateTrack(result, deviceSerialNumber, type);
|
||||
track.add(fix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LiveDataConnection createLiveConnection(Iterable<String> deviceSerialNumbers) throws Exception {
|
||||
return new WebSocketConnectionManager(connectionFactory, deviceSerialNumbers, getAccount());
|
||||
}
|
||||
|
||||
private DynamicTrack<Fix> getOrCreateTrack(Map<String, Map<Type, DynamicTrack<Fix>>> result,
|
||||
String deviceSerialNumber, Type type) {
|
||||
Map<Type, DynamicTrack<Fix>> mapForDevice = result.get(deviceSerialNumber);
|
||||
if (mapForDevice == null) {
|
||||
mapForDevice = new HashMap<>();
|
||||
result.put(deviceSerialNumber, mapForDevice);
|
||||
}
|
||||
DynamicTrack<Fix> track = mapForDevice.get(type);
|
||||
if (track == null) {
|
||||
track = new DynamicTrackImpl<Fix>("Track for Igtimi fixes of type "+type.name()+" for "+this);
|
||||
mapForDevice.put(type, track);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Device> getOwnedDevices() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getResources = new HttpGet(connectionFactory.getOwnedDevicesUrl(account));
|
||||
JSONObject devicesJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResources));
|
||||
final List<Device> result = new ArrayList<>();
|
||||
for (Object deviceJson : (JSONArray) devicesJson.get("devices")) {
|
||||
Device device = new DeviceDeserializer().createResourceFromJson((JSONObject) ((JSONObject) deviceJson).get("device"), this);
|
||||
result.add(device);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<DataAccessWindow> getDataAccessWindows(Permission permission, TimePoint startTime,
|
||||
TimePoint endTime, Iterable<String> deviceSerialNumbers) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
HttpClient client = connectionFactory.getHttpClient();
|
||||
HttpGet getDataAccessWindows = new HttpGet(connectionFactory.getDataAccessWindowsUrl(permission, startTime, endTime, deviceSerialNumbers, account));
|
||||
JSONObject dataAccessWindowsJson = ConnectivityUtils.getJsonFromResponse(client.execute(getDataAccessWindows));
|
||||
final List<DataAccessWindow> result = new ArrayList<>();
|
||||
for (Object dataAccessWindowJson : (JSONArray) dataAccessWindowsJson.get("data_access_windows")) {
|
||||
DataAccessWindow dataAccessWindow = new DataAccessWindowDeserializer().createDataAccessWindowFromJson(
|
||||
(JSONObject) ((JSONObject) dataAccessWindowJson).get("data_access_window"), this);
|
||||
result.add(dataAccessWindow);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.declination.Declination;
|
||||
import com.sap.sailing.declination.DeclinationService;
|
||||
import com.sap.sailing.domain.base.Timed;
|
||||
import com.sap.sailing.domain.base.impl.ScalableBearing;
|
||||
import com.sap.sailing.domain.base.impl.ScalablePosition;
|
||||
import com.sap.sailing.domain.base.impl.ScalableSpeed;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
import com.sap.sailing.domain.confidence.ScalableValue;
|
||||
import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiverAdapter;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.COG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDG;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.SOG;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindListener;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
|
||||
/**
|
||||
* Receives Igtimi {@link Fix}es and tries to generate a {@link Wind} object from each {@link AWS} fix. For this to
|
||||
* work, the time-wise adjacent {@link AWA}, {@link HDG}/{@link HDGM} and {@link GpsLatLong} fixes are used. If only
|
||||
* a magnetic heading ({@link HDGM}) is available, the {@link DeclinationService} is used to map that to a true heading.
|
||||
* The true wind direction is determined by adding the boat speed vector onto the apparent wind vector.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class IgtimiWindReceiver implements BulkFixReceiver {
|
||||
private static final Logger logger = Logger.getLogger(IgtimiWindReceiver.class.getName());
|
||||
private final DynamicTrack<AWA> awaTrack;
|
||||
private final DynamicTrack<AWS> awsTrack;
|
||||
private final DynamicTrack<GpsLatLong> gpsTrack;
|
||||
private final DynamicTrack<COG> cogTrack;
|
||||
private final DynamicTrack<SOG> sogTrack;
|
||||
private final DynamicTrack<HDG> hdgTrack;
|
||||
private final DynamicTrack<HDGM> hdgmTrack;
|
||||
private final FixReceiver receiver;
|
||||
private final DeclinationService declinationService;
|
||||
private final ConcurrentHashMap<WindListener, WindListener> listeners;
|
||||
|
||||
private class FixReceiver extends IgtimiFixReceiverAdapter {
|
||||
@Override
|
||||
public void received(AWA fix) {
|
||||
getAwaTrack().add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(AWS fix) {
|
||||
getAwsTrack().add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(GpsLatLong fix) {
|
||||
getGpsTrack().add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(COG fix) {
|
||||
getCogTrack().add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(SOG fix) {
|
||||
getSogTrack().add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(HDG fix) {
|
||||
getHdgTrack().add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(HDGM fix) {
|
||||
getHdgmTrack().add(fix);
|
||||
}
|
||||
}
|
||||
|
||||
public IgtimiWindReceiver(String deviceSerialNumber) {
|
||||
receiver = new FixReceiver();
|
||||
declinationService = DeclinationService.INSTANCE;
|
||||
listeners = new ConcurrentHashMap<>();
|
||||
awaTrack = new DynamicTrackImpl<>("AWA Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
awsTrack = new DynamicTrackImpl<>("AWS Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
gpsTrack = new DynamicTrackImpl<>("GPS Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
cogTrack = new DynamicTrackImpl<>("COG Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
sogTrack = new DynamicTrackImpl<>("SOG Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
hdgTrack = new DynamicTrackImpl<>("HDG Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
hdgmTrack = new DynamicTrackImpl<>("HDGM Track for Igtimi wind track for device "+deviceSerialNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a batch of fixes was received, e.g., after loading from stored data or from a live web socket receiver.
|
||||
* The fixes are pumped into the tracks by the superclass implementation. Then, for all new {@link AWS} fixes, a
|
||||
* call to {@link IgtimiWindReceiver#getWind} is performed.
|
||||
*/
|
||||
@Override
|
||||
public void received(Iterable<Fix> fixes) {
|
||||
final List<AWS> awsFixes = new ArrayList<>();
|
||||
for (Fix fix : fixes) {
|
||||
fix.notify(receiver);
|
||||
fix.notify(new IgtimiFixReceiverAdapter() {
|
||||
@Override
|
||||
public void received(AWS fix) {
|
||||
awsFixes.add(fix);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (AWS aws : awsFixes) {
|
||||
try {
|
||||
final Wind wind = getWind(aws.getTimePoint());
|
||||
if (wind != null) {
|
||||
notifyListeners(wind);
|
||||
}
|
||||
} catch (ClassNotFoundException | IOException | ParseException e) {
|
||||
logger.log(Level.INFO, "Exception while trying to construct Wind fix from Igtimi fix " + aws, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(WindListener listener) {
|
||||
listeners.put(listener, listener);
|
||||
}
|
||||
|
||||
public void notifyListeners(Wind wind) {
|
||||
for (WindListener listener : listeners.keySet()) {
|
||||
listener.windDataReceived(wind);
|
||||
}
|
||||
}
|
||||
|
||||
private Wind getWind(TimePoint timePoint) throws ClassNotFoundException, IOException, ParseException {
|
||||
final Wind result;
|
||||
Pair<AWA, AWA> awaPair = getSurroundingFixes(getAwaTrack(), timePoint);
|
||||
Bearing awa = getAWA(timePoint, awaPair);
|
||||
Pair<AWS, AWS> awsPair = getSurroundingFixes(getAwsTrack(), timePoint);
|
||||
Speed aws = getAWS(timePoint, awsPair);
|
||||
Pair<GpsLatLong, GpsLatLong> gpsPair = getSurroundingFixes(getGpsTrack(), timePoint);
|
||||
Position pos = getPosition(timePoint, gpsPair);
|
||||
Pair<HDG, HDG> hdgPair = getSurroundingFixes(getHdgTrack(), timePoint);
|
||||
Pair<HDGM, HDGM> hdgmPair = getSurroundingFixes(getHdgmTrack(), timePoint);
|
||||
Bearing heading = getHeading(timePoint, hdgPair, hdgmPair, pos);
|
||||
if (awa != null && aws != null && pos != null && heading != null) {
|
||||
Bearing apparentWindDirection = heading.add(awa);
|
||||
SpeedWithBearing apparentWindSpeedWithDirection = new KnotSpeedWithBearingImpl(aws.getKnots(), apparentWindDirection);
|
||||
/*
|
||||
* Hint from Brent Russell from Igtimi, at 2013-12-05 on the question whether to use GpsLatLong to improve
|
||||
* precision of boat speed / coarse over SOG/COG measurements:
|
||||
*
|
||||
* "Personally I would use COG/SOG exclusively, and if unhappy with the result add a small amount of
|
||||
* smoothing and consider dropping samples as outliers if they cause a SOG discontinuity. The latter might
|
||||
* happen as a satellite is dropped/acquired - and I'd expect to see a time correlated position jump as
|
||||
* well. Probably not though a direction/speed correlation :)
|
||||
*
|
||||
* All our GPS systems are using Doppler to calculate COG/SOG and this should be the most accurate measure.
|
||||
* I don't believe that delta position really adds any more "truth" to the measurement of physical reality,
|
||||
* if that makes sense. I'd trust d-p even less at low speeds, where you'll see the most disagreement. Also
|
||||
* there is a significant quantisation noise error in the d-p calculations from the GPS resolution too, so
|
||||
* you'd have to smooth it before averaging - possibly in a speed dependent way.
|
||||
*
|
||||
* Remember that Doppler COG/SOG is using the same raw satellite measurements that are being used to
|
||||
* calculate position, just the algorithm is different. I suspect that merging the two might be, in
|
||||
* practice, just a slightly indirect way of averaging. If you like the central limit theorem in action over
|
||||
* the set of algorithms!
|
||||
*
|
||||
* So again, my personal preference would be to work with the data that should be the most accurate
|
||||
* (COG/SOG) and consider algorithms that handle smoothing of that data best."
|
||||
*/
|
||||
Pair<SOG, SOG> sogPair = getSurroundingFixes(getSogTrack(), timePoint);
|
||||
Speed sog = getSOG(timePoint, sogPair);
|
||||
Pair<COG, COG> cogPair = getSurroundingFixes(getCogTrack(), timePoint);
|
||||
Bearing cog = getCOG(timePoint, cogPair);
|
||||
SpeedWithBearing sogCog = new KnotSpeedWithBearingImpl(sog.getKnots(), cog);
|
||||
SpeedWithBearing trueWindSpeedAndDirection = apparentWindSpeedWithDirection.add(sogCog);
|
||||
result = new WindImpl(pos, timePoint, trueWindSpeedAndDirection);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Bearing getAWA(TimePoint timePoint, Pair<AWA, AWA> awaPair) {
|
||||
final Bearing awaFrom;
|
||||
if (awaPair.getA() == null) {
|
||||
if (awaPair.getB() == null) {
|
||||
awaFrom = null;
|
||||
} else {
|
||||
awaFrom = awaPair.getB().getApparentWindAngle();
|
||||
}
|
||||
} else {
|
||||
if (awaPair.getB() == null) {
|
||||
awaFrom = awaPair.getA().getApparentWindAngle();
|
||||
} else {
|
||||
awaFrom = timeBasedAgerage(timePoint,
|
||||
new ScalableBearing(awaPair.getA().getApparentWindAngle()), awaPair.getA().getTimePoint(),
|
||||
new ScalableBearing(awaPair.getB().getApparentWindAngle()), awaPair.getB().getTimePoint());
|
||||
}
|
||||
}
|
||||
return awaFrom == null ? null : awaFrom.reverse();
|
||||
}
|
||||
|
||||
private Speed getAWS(TimePoint timePoint, Pair<AWS, AWS> awsPair) {
|
||||
final Speed aws;
|
||||
if (awsPair.getA() == null) {
|
||||
if (awsPair.getB() == null) {
|
||||
aws = null;
|
||||
} else {
|
||||
aws = awsPair.getB().getApparentWindSpeed();
|
||||
}
|
||||
} else {
|
||||
if (awsPair.getB() == null) {
|
||||
aws = awsPair.getA().getApparentWindSpeed();
|
||||
} else {
|
||||
aws = timeBasedAgerage(timePoint,
|
||||
new ScalableSpeed(awsPair.getA().getApparentWindSpeed()), awsPair.getA().getTimePoint(),
|
||||
new ScalableSpeed(awsPair.getB().getApparentWindSpeed()), awsPair.getB().getTimePoint());
|
||||
}
|
||||
}
|
||||
return aws;
|
||||
}
|
||||
|
||||
private Speed getSOG(TimePoint timePoint, Pair<SOG, SOG> sogPair) {
|
||||
final Speed sog;
|
||||
if (sogPair.getA() == null) {
|
||||
if (sogPair.getB() == null) {
|
||||
sog = null;
|
||||
} else {
|
||||
sog = sogPair.getB().getSpeedOverGround();
|
||||
}
|
||||
} else {
|
||||
if (sogPair.getB() == null) {
|
||||
sog = sogPair.getA().getSpeedOverGround();
|
||||
} else {
|
||||
sog = timeBasedAgerage(timePoint,
|
||||
new ScalableSpeed(sogPair.getA().getSpeedOverGround()), sogPair.getA().getTimePoint(),
|
||||
new ScalableSpeed(sogPair.getB().getSpeedOverGround()), sogPair.getB().getTimePoint());
|
||||
}
|
||||
}
|
||||
return sog;
|
||||
}
|
||||
|
||||
private Bearing getCOG(TimePoint timePoint, Pair<COG, COG> cogPair) {
|
||||
final Bearing sog;
|
||||
if (cogPair.getA() == null) {
|
||||
if (cogPair.getB() == null) {
|
||||
sog = null;
|
||||
} else {
|
||||
sog = cogPair.getB().getCourseOverGround();
|
||||
}
|
||||
} else {
|
||||
if (cogPair.getB() == null) {
|
||||
sog = cogPair.getA().getCourseOverGround();
|
||||
} else {
|
||||
sog = timeBasedAgerage(timePoint,
|
||||
new ScalableBearing(cogPair.getA().getCourseOverGround()), cogPair.getA().getTimePoint(),
|
||||
new ScalableBearing(cogPair.getB().getCourseOverGround()), cogPair.getB().getTimePoint());
|
||||
}
|
||||
}
|
||||
return sog;
|
||||
}
|
||||
|
||||
private Position getPosition(TimePoint timePoint, Pair<GpsLatLong, GpsLatLong> gpsPair) {
|
||||
final Position pos;
|
||||
if (gpsPair.getA() == null) {
|
||||
if (gpsPair.getB() == null) {
|
||||
pos = null;
|
||||
} else {
|
||||
pos = gpsPair.getB().getPosition();
|
||||
}
|
||||
} else {
|
||||
if (gpsPair.getB() == null) {
|
||||
pos = gpsPair.getA().getPosition();
|
||||
} else {
|
||||
pos = timeBasedAgerage(timePoint, new ScalablePosition(gpsPair.getA().getPosition()), gpsPair.getA()
|
||||
.getTimePoint(), new ScalablePosition(gpsPair.getB().getPosition()), gpsPair.getB()
|
||||
.getTimePoint());
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private Bearing getHDG(TimePoint timePoint, Pair<HDG, HDG> hdgPair) {
|
||||
final Bearing hdg;
|
||||
if (hdgPair.getA() == null) {
|
||||
if (hdgPair.getB() == null) {
|
||||
hdg = null;
|
||||
} else {
|
||||
hdg = hdgPair.getB().getTrueHeading();
|
||||
}
|
||||
} else {
|
||||
if (hdgPair.getB() == null) {
|
||||
hdg = hdgPair.getA().getTrueHeading();
|
||||
} else {
|
||||
hdg = timeBasedAgerage(timePoint,
|
||||
new ScalableBearing(hdgPair.getA().getTrueHeading()), hdgPair.getA().getTimePoint(),
|
||||
new ScalableBearing(hdgPair.getB().getTrueHeading()), hdgPair.getB().getTimePoint());
|
||||
}
|
||||
}
|
||||
return hdg;
|
||||
}
|
||||
|
||||
private Bearing getHDGM(TimePoint timePoint, Pair<HDGM, HDGM> hdgmPair) {
|
||||
final Bearing hdgm;
|
||||
if (hdgmPair.getA() == null) {
|
||||
if (hdgmPair.getB() == null) {
|
||||
hdgm = null;
|
||||
} else {
|
||||
hdgm = hdgmPair.getB().getMagnetigHeading();
|
||||
}
|
||||
} else {
|
||||
if (hdgmPair.getB() == null) {
|
||||
hdgm = hdgmPair.getA().getMagnetigHeading();
|
||||
} else {
|
||||
hdgm = timeBasedAgerage(timePoint,
|
||||
new ScalableBearing(hdgmPair.getA().getMagnetigHeading()), hdgmPair.getA().getTimePoint(),
|
||||
new ScalableBearing(hdgmPair.getB().getMagnetigHeading()), hdgmPair.getB().getTimePoint());
|
||||
}
|
||||
}
|
||||
return hdgm;
|
||||
}
|
||||
|
||||
private Bearing getHeading(TimePoint timePoint, Pair<HDG, HDG> hdgPair, Pair<HDGM, HDGM> hdgmPair, Position position) throws ClassNotFoundException, IOException, ParseException {
|
||||
final Bearing trueHeading;
|
||||
Bearing hdg = getHDG(timePoint, hdgPair);
|
||||
if (hdg != null) {
|
||||
trueHeading = hdg;
|
||||
} else {
|
||||
Bearing hdgm = getHDGM(timePoint, hdgmPair);
|
||||
Declination declination = declinationService.getDeclination(timePoint, position, /* timeoutForOnlineFetchInMilliseconds 5s */ 5000);
|
||||
trueHeading = hdgm.add(declination.getBearingCorrectedTo(timePoint));
|
||||
}
|
||||
return trueHeading;
|
||||
}
|
||||
|
||||
private <V, T> T timeBasedAgerage(TimePoint timePoint, ScalableValue<V, T> value1, TimePoint timePoint1, ScalableValue<V, T> value2, TimePoint timePoint2) {
|
||||
final T acc;
|
||||
if (timePoint1.equals(timePoint2)) {
|
||||
acc = value1.add(value2).divide(2);
|
||||
} else {
|
||||
long timeDiff1 = Math.abs(timePoint1.asMillis() - timePoint.asMillis());
|
||||
long timeDiff2 = Math.abs(timePoint2.asMillis() - timePoint.asMillis());
|
||||
acc = value1.multiply(timeDiff2).add(value2.multiply(timeDiff1)).divide(timeDiff1 + timeDiff2);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
private <T extends Timed> Pair<T, T> getSurroundingFixes(Track<T> track, TimePoint timePoint) {
|
||||
T left = track.getLastFixAtOrBefore(timePoint);
|
||||
T right = track.getFirstFixAtOrAfter(timePoint);
|
||||
Pair<T, T> result = new Pair<>(left, right);
|
||||
return result;
|
||||
}
|
||||
|
||||
private DynamicTrack<AWA> getAwaTrack() {
|
||||
return awaTrack;
|
||||
}
|
||||
|
||||
private DynamicTrack<AWS> getAwsTrack() {
|
||||
return awsTrack;
|
||||
}
|
||||
|
||||
private DynamicTrack<GpsLatLong> getGpsTrack() {
|
||||
return gpsTrack;
|
||||
}
|
||||
|
||||
private DynamicTrack<COG> getCogTrack() {
|
||||
return cogTrack;
|
||||
}
|
||||
|
||||
private DynamicTrack<SOG> getSogTrack() {
|
||||
return sogTrack;
|
||||
}
|
||||
|
||||
private DynamicTrack<HDG> getHdgTrack() {
|
||||
return hdgTrack;
|
||||
}
|
||||
|
||||
private DynamicTrack<HDGM> getHdgmTrack() {
|
||||
return hdgmTrack;
|
||||
}
|
||||
}
|
||||
+4
-18
@@ -1,18 +1,14 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
|
||||
public class ResourceDeserializer {
|
||||
public Resource createResourceFromJson(JSONObject resourceJson) {
|
||||
public class ResourceDeserializer extends HasPermissionsDeserializer {
|
||||
public Resource createResourceFromJson(JSONObject resourceJson, IgtimiConnection conn) {
|
||||
Boolean blob = (Boolean) resourceJson.get("blob");
|
||||
return new ResourceImpl((Long) resourceJson.get("id"),
|
||||
new MillisecondsTimePoint(((Double) resourceJson.get("start_time")).longValue()),
|
||||
@@ -20,17 +16,7 @@ public class ResourceDeserializer {
|
||||
(String) resourceJson.get("device_serial_number"),
|
||||
getDataTypes((JSONArray) resourceJson.get("data_types")),
|
||||
getPermissions((JSONObject) resourceJson.get("permissions")),
|
||||
blob == null ? false : blob);
|
||||
}
|
||||
|
||||
private Iterable<Permission> getPermissions(JSONObject permissions) {
|
||||
final List<Permission> result = new ArrayList<>();
|
||||
for (Entry<Object, Object> e : permissions.entrySet()) {
|
||||
if ((Boolean) e.getValue()) {
|
||||
result.add(Permission.valueOf((String) e.getKey()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
blob == null ? false : blob, conn);
|
||||
}
|
||||
|
||||
private int[] getDataTypes(JSONArray jsonArray) {
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Resource;
|
||||
|
||||
@@ -14,7 +15,7 @@ public class ResourceImpl implements Resource {
|
||||
private final boolean blob;
|
||||
|
||||
public ResourceImpl(long id, TimePoint startTime, TimePoint endTime, String deviceSerialNumber, int[] dataTypes,
|
||||
Iterable<Permission> permissions, boolean blob) {
|
||||
Iterable<Permission> permissions, boolean blob, IgtimiConnection conn) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.startTime = startTime;
|
||||
|
||||
+17
-19
@@ -3,35 +3,36 @@ package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
import com.sap.sailing.domain.igtimiadapter.Sensor;
|
||||
|
||||
public class SensorImpl implements Sensor {
|
||||
private final String transmitterId;
|
||||
private final String deviceId;
|
||||
protected SensorImpl(String transmitterId, String deviceId) {
|
||||
private final String deviceSerialNumber;
|
||||
private final long subDeviceId;
|
||||
|
||||
protected SensorImpl(String deviceSerialNumber, long subDeviceId) {
|
||||
super();
|
||||
this.transmitterId = transmitterId;
|
||||
this.deviceId = deviceId;
|
||||
this.deviceSerialNumber = deviceSerialNumber;
|
||||
this.subDeviceId = subDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransmitterId() {
|
||||
return transmitterId;
|
||||
public String getDeviceSerialNumber() {
|
||||
return deviceSerialNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
public long getSensorId() {
|
||||
return subDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Transmitter "+getTransmitterId()+(getDeviceId()==null?"":(", device "+getDeviceId()));
|
||||
return "Device "+getDeviceSerialNumber()+", sensor "+getSensorId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((deviceId == null) ? 0 : deviceId.hashCode());
|
||||
result = prime * result + ((transmitterId == null) ? 0 : transmitterId.hashCode());
|
||||
result = prime * result + ((deviceSerialNumber == null) ? 0 : deviceSerialNumber.hashCode());
|
||||
result = prime * result + (int) (subDeviceId ^ (subDeviceId >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -44,15 +45,12 @@ public class SensorImpl implements Sensor {
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SensorImpl other = (SensorImpl) obj;
|
||||
if (deviceId == null) {
|
||||
if (other.deviceId != null)
|
||||
if (deviceSerialNumber == null) {
|
||||
if (other.deviceSerialNumber != null)
|
||||
return false;
|
||||
} else if (!deviceId.equals(other.deviceId))
|
||||
} else if (!deviceSerialNumber.equals(other.deviceSerialNumber))
|
||||
return false;
|
||||
if (transmitterId == null) {
|
||||
if (other.transmitterId != null)
|
||||
return false;
|
||||
} else if (!transmitterId.equals(other.transmitterId))
|
||||
if (subDeviceId != other.subDeviceId)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.Session;
|
||||
|
||||
public class SessionDeserializer extends HasPermissionsDeserializer {
|
||||
public Session createResourceFromJson(JSONObject sessionJson, IgtimiConnection conn) {
|
||||
Boolean blob = (Boolean) sessionJson.get("blob");
|
||||
final Double startTime = (Double) sessionJson.get("start_time");
|
||||
final Double endTime = (Double) sessionJson.get("end_time");
|
||||
return new SessionImpl((Long) sessionJson.get("id"), (String) sessionJson.get("name"), (Long) sessionJson.get("owner_id"),
|
||||
(Long) sessionJson.get("session_group_id"), (Long) sessionJson.get("admin_session_group_id"),
|
||||
getPermissions((JSONObject) sessionJson.get("permissions")),
|
||||
startTime==null?null:new MillisecondsTimePoint(startTime.longValue()),
|
||||
endTime==null?null:new MillisecondsTimePoint(endTime.longValue()),
|
||||
blob == null ? false : blob, conn);
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.sap.sailing.domain.igtimiadapter.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.Permission;
|
||||
import com.sap.sailing.domain.igtimiadapter.Session;
|
||||
import com.sap.sailing.domain.igtimiadapter.User;
|
||||
|
||||
public class SessionImpl extends HasIdImpl implements Session {
|
||||
private final long ownerId;
|
||||
private final String name;
|
||||
private final long sessionGroupId;
|
||||
private final long adminSessionGroupId;
|
||||
private final Iterable<Permission> permissions;
|
||||
private final TimePoint startTime;
|
||||
private final TimePoint endTime;
|
||||
private final boolean blob;
|
||||
private final IgtimiConnection conn;
|
||||
|
||||
protected SessionImpl(long id, String name, long ownerId, long sessionGroupId,
|
||||
long adminSessionGroupId, Iterable<Permission> permissions, TimePoint startTime, TimePoint endTime, boolean blob, IgtimiConnection conn) {
|
||||
super(id);
|
||||
this.name = name;
|
||||
this.ownerId = ownerId;
|
||||
this.sessionGroupId = sessionGroupId;
|
||||
this.adminSessionGroupId = adminSessionGroupId;
|
||||
this.permissions = permissions;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.blob = blob;
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSessionGroupId() {
|
||||
return sessionGroupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAdminSessionGroupId() {
|
||||
return adminSessionGroupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlob() {
|
||||
return blob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getOwner() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
|
||||
return conn.getUser(getOwnerId());
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -7,12 +7,11 @@ import com.sap.sailing.domain.igtimiadapter.User;
|
||||
public class UserDeserializer {
|
||||
public User createUserFromJson(JSONObject userJson) {
|
||||
Boolean blob = (Boolean) userJson.get("blob");
|
||||
return new UserImpl((Long) userJson.get("id"),
|
||||
final Long id = (Long) userJson.get("id");
|
||||
return new UserImpl(id,
|
||||
(String) userJson.get("first_name"),
|
||||
(String) userJson.get("surname"),
|
||||
(String) userJson.get("email"),
|
||||
blob == null ? false : blob);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+2
-2
@@ -17,8 +17,8 @@ import javax.ws.rs.core.UriInfo;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectionFactoryImpl;
|
||||
|
||||
@Path(AuthorizationCallback.V1)
|
||||
public class AuthorizationCallback {
|
||||
@@ -27,7 +27,7 @@ public class AuthorizationCallback {
|
||||
private static final String AUTHORIZATIONCALLBACK = "/authorizationcallback";
|
||||
static final String V1 = "/v1";
|
||||
static final String V1_AUTHORIZATIONCALLBACK = V1 + AUTHORIZATIONCALLBACK;
|
||||
private final IgtimiConnectionFactory connectionFactory;
|
||||
private final IgtimiConnectionFactoryImpl connectionFactory;
|
||||
|
||||
public AuthorizationCallback() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
|
||||
connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
|
||||
+21
-19
@@ -5,10 +5,9 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -26,14 +25,15 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
import com.sap.sailing.domain.igtimiadapter.Account;
|
||||
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.LiveDataListener;
|
||||
import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver;
|
||||
import com.sap.sailing.domain.igtimiadapter.LiveDataConnection;
|
||||
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.FixFactory;
|
||||
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectionFactoryImpl;
|
||||
|
||||
public class WebSocketConnectionManager extends WebSocketAdapter {
|
||||
public class WebSocketConnectionManager extends WebSocketAdapter implements LiveDataConnection {
|
||||
private static final Logger logger = Logger.getLogger(WebSocketConnectionManager.class.getName());
|
||||
private final IgtimiConnectionFactory connectionFactory;
|
||||
private final IgtimiConnectionFactoryImpl connectionFactory;
|
||||
private static enum TargetState { OPEN, CLOSED };
|
||||
private TargetState targetState;
|
||||
private final WebSocketClient client;
|
||||
@@ -44,19 +44,19 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
||||
private final Account account;
|
||||
private final FixFactory fixFactory;
|
||||
private boolean receivedServerHeartbeatInInterval;
|
||||
private final Set<LiveDataListener> liveDataListeners;
|
||||
private final ConcurrentHashMap<BulkFixReceiver, BulkFixReceiver> listeners;
|
||||
private TimePoint igtimiServerTimepoint;
|
||||
private TimePoint localTimepointWhenServerTimepointWasReceived;
|
||||
|
||||
public WebSocketConnectionManager(IgtimiConnectionFactory connectionFactory, Iterable<String> deviceIds, Account account) throws Exception {
|
||||
this.timer = new Timer("Timer for WebSocketConnectionManager for units "+deviceIds+" and account "+account);
|
||||
this.deviceIds = deviceIds;
|
||||
public WebSocketConnectionManager(IgtimiConnectionFactoryImpl connectionFactory, Iterable<String> deviceSerialNumbers, Account account) throws Exception {
|
||||
this.timer = new Timer("Timer for WebSocketConnectionManager for units "+deviceSerialNumbers+" and account "+account);
|
||||
this.deviceIds = deviceSerialNumbers;
|
||||
this.account = account;
|
||||
this.fixFactory = new FixFactory();
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.liveDataListeners = new ConcurrentSkipListSet<>();
|
||||
this.listeners = new ConcurrentHashMap<>();
|
||||
client = new WebSocketClient();
|
||||
configurationMessage = connectionFactory.getWebSocketConfigurationMessage(account, deviceIds);
|
||||
configurationMessage = connectionFactory.getWebSocketConfigurationMessage(account, deviceSerialNumbers);
|
||||
request = new ClientUpgradeRequest();
|
||||
client.start();
|
||||
reconnect();
|
||||
@@ -71,6 +71,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
||||
* use 0 to wait indefinitely
|
||||
* @return <code>true</code> if the connection is established before the timeout occurred
|
||||
*/
|
||||
@Override
|
||||
public boolean waitForConnection(long timeoutInMillis) throws InterruptedException {
|
||||
long startedToWait = System.currentTimeMillis();
|
||||
synchronized (this) {
|
||||
@@ -81,10 +82,6 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
public void addLiveDataListener(LiveDataListener listener) {
|
||||
liveDataListeners.add(listener);
|
||||
}
|
||||
|
||||
public void disconnect() throws Exception {
|
||||
targetState = TargetState.CLOSED;
|
||||
client.stop();
|
||||
@@ -141,9 +138,14 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
||||
return new Pair<TimePoint, TimePoint>(igtimiServerTimepoint, localTimepointWhenServerTimepointWasReceived);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(BulkFixReceiver listener) {
|
||||
listeners.put(listener, listener);
|
||||
}
|
||||
|
||||
private void notifyListeners(List<Fix> fixes) {
|
||||
for (LiveDataListener listener : liveDataListeners) {
|
||||
listener.fixesReceived(fixes);
|
||||
for (BulkFixReceiver listener : listeners.keySet()) {
|
||||
listener.received(fixes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
||||
}, /* delay */ 0, /* send ping message every other minute; has to be at least every five minutes, so this should be safe */ 120000l);
|
||||
}
|
||||
|
||||
public void reconnect() throws IOException, IllegalStateException, ParseException, URISyntaxException {
|
||||
private void reconnect() throws IOException, IllegalStateException, ParseException, URISyntaxException {
|
||||
IOException lastException = null;
|
||||
for (URI uri : connectionFactory.getWebsocketServers()) {
|
||||
try {
|
||||
|
||||
+5
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
@@ -10,6 +11,8 @@ import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.RegattaRegistry;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
@@ -97,4 +100,6 @@ public interface DomainObjectFactory {
|
||||
Collection<Competitor> loadAllCompetitors();
|
||||
|
||||
DomainFactory getBaseDomainFactory();
|
||||
|
||||
Iterable<Entry<DeviceConfigurationMatcher, DeviceConfiguration>> loadAllDeviceConfigurations();
|
||||
}
|
||||
+6
@@ -9,6 +9,8 @@ import com.sap.sailing.domain.base.Event;
|
||||
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.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
@@ -102,4 +104,8 @@ public interface MongoObjectFactory {
|
||||
|
||||
DB getDatabase();
|
||||
|
||||
void storeDeviceConfiguration(DeviceConfigurationMatcher matcher, DeviceConfiguration configuration);
|
||||
|
||||
void removeDeviceConfiguration(DeviceConfigurationMatcher matcher);
|
||||
|
||||
}
|
||||
+7
-1
@@ -51,10 +51,16 @@ public enum CollectionNames {
|
||||
/**
|
||||
* Stores the race log events for a tracked race.
|
||||
*/
|
||||
RACE_LOGS,
|
||||
|
||||
|
||||
/**
|
||||
* Stores competitors for smartphone tracking.
|
||||
*/
|
||||
COMPETITORS,
|
||||
RACE_LOGS,
|
||||
|
||||
/**
|
||||
* Stores configurations for mobile devices.
|
||||
*/
|
||||
CONFIGURATIONS;
|
||||
}
|
||||
+167
-63
@@ -7,6 +7,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
@@ -15,6 +16,7 @@ import java.util.logging.Logger;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -41,6 +43,11 @@ import com.sap.sailing.domain.base.RegattaRegistry;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Venue;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
|
||||
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
|
||||
import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationImpl;
|
||||
import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseDataImpl;
|
||||
import com.sap.sailing.domain.base.impl.EventImpl;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
@@ -61,6 +68,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.configuration.DeviceConfigurationMatcherType;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
@@ -72,7 +80,7 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
|
||||
import com.sap.sailing.domain.common.racelog.StartProcedureType;
|
||||
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.domain.leaderboard.DelayedLeaderboardCorrections;
|
||||
import com.sap.sailing.domain.leaderboard.DelayedLeaderboardCorrections.LeaderboardCorrectionsResolvedListener;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
@@ -90,10 +98,12 @@ import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRul
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
|
||||
import com.sap.sailing.domain.racelog.CompetitorResults;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogCourseAreaChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventRestoreFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningConfirmedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningListChangedEvent;
|
||||
@@ -108,14 +118,20 @@ import com.sap.sailing.domain.racelog.RaceLogStartProcedureChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
import com.sap.sailing.domain.racelog.RaceLogWindFixEvent;
|
||||
import com.sap.sailing.domain.racelog.impl.CompetitorResultsImpl;
|
||||
import com.sap.sailing.domain.racelog.impl.RaceLogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.racelog.impl.RaceLogImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.DeviceConfigurationJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.RegattaConfigurationJsonDeserializer;
|
||||
|
||||
public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName());
|
||||
@@ -839,7 +855,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
|
||||
private CourseArea loadCourseArea(DBObject courseAreaDBObject) {
|
||||
String name = (String) courseAreaDBObject.get(FieldNames.COURSE_AREA_NAME.name());
|
||||
Serializable id = (Serializable) courseAreaDBObject.get(FieldNames.COURSE_AREA_ID.name());
|
||||
UUID id = (UUID) courseAreaDBObject.get(FieldNames.COURSE_AREA_ID.name());
|
||||
return baseDomainFactory.getOrCreateCourseArea(id, name);
|
||||
}
|
||||
|
||||
@@ -890,7 +906,18 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
|
||||
}
|
||||
|
||||
RegattaConfiguration configuration = null;
|
||||
if (dbRegatta.containsField(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())) {
|
||||
try {
|
||||
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(dbRegatta.get(FieldNames.REGATTA_REGATTA_CONFIGURATION.name()))));
|
||||
configuration = RegattaConfigurationJsonDeserializer.create().deserialize(json);
|
||||
} catch (JsonDeserializationException|ParseException e) {
|
||||
logger.log(Level.WARNING, "Error loading racing procedure configration for regatta.", e);
|
||||
}
|
||||
}
|
||||
|
||||
result = new RegattaImpl(raceLogStore, baseName, boatClass, series, /* persistent */ true, loadScoringScheme(dbRegatta), id, courseArea);
|
||||
result.setRegattaConfiguration(configuration);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1022,9 +1049,19 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), TripleSerializer.serialize(identifier.getIdentifier()));
|
||||
loadRaceLogEvents(result, query);
|
||||
|
||||
// query for events with deprecated identifier format
|
||||
// query for events with deprecated identifier format...
|
||||
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(identifier.getDeprecatedIdentifier()));
|
||||
loadRaceLogEvents(result, query);
|
||||
|
||||
List<RaceLogEvent> eventsToMigrate = loadRaceLogEvents(result, query);
|
||||
if (!eventsToMigrate.isEmpty()) {
|
||||
// ... migrate them...
|
||||
MongoRaceLogStoreVisitor storeVisitor = new MongoRaceLogStoreVisitor(identifier, new MongoObjectFactoryImpl(database));
|
||||
for (RaceLogEvent event : eventsToMigrate) {
|
||||
event.accept(storeVisitor);
|
||||
}
|
||||
// ... and delete the old ones...
|
||||
database.getCollection(CollectionNames.RACE_LOGS.name()).remove(query);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// something went wrong during DB access; report, then use empty new race log
|
||||
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load recorded race log data. Check MongoDB settings.");
|
||||
@@ -1033,126 +1070,141 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void loadRaceLogEvents(RaceLog result, BasicDBObject query) {
|
||||
private List<RaceLogEvent> loadRaceLogEvents(RaceLog targetRaceLog, BasicDBObject query) {
|
||||
List<RaceLogEvent> result = new ArrayList<>();
|
||||
DBCollection raceLog = database.getCollection(CollectionNames.RACE_LOGS.name());
|
||||
for (DBObject o : raceLog.find(query)) {
|
||||
RaceLogEvent raceLogEvent = loadRaceLogEvent((DBObject) o.get(FieldNames.RACE_LOG_EVENT.name()));
|
||||
if (raceLogEvent != null) {
|
||||
result.load(raceLogEvent);
|
||||
targetRaceLog.load(raceLogEvent);
|
||||
result.add(raceLogEvent);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public RaceLogEvent loadRaceLogEvent(DBObject dbObject) {
|
||||
TimePoint timePoint = loadTimePoint(dbObject);
|
||||
TimePoint logicalTimePoint = loadTimePoint(dbObject);
|
||||
TimePoint createdAt = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_CREATED_AT);
|
||||
Serializable id = (Serializable) dbObject.get(FieldNames.RACE_LOG_EVENT_ID.name());
|
||||
Integer passId = (Integer) dbObject.get(FieldNames.RACE_LOG_EVENT_PASS_ID.name());
|
||||
BasicDBList dbCompetitors = (BasicDBList) dbObject.get(FieldNames.RACE_LOG_EVENT_INVOLVED_BOATS.name());
|
||||
List<Competitor> competitors = loadCompetitorsForRaceLogEvent(dbCompetitors);
|
||||
final RaceLogEventAuthor author;
|
||||
String authorName = (String) dbObject.get(FieldNames.RACE_LOG_EVENT_AUTHOR_NAME.name());
|
||||
Number authorPriority = (Number) dbObject.get(FieldNames.RACE_LOG_EVENT_AUTHOR_PRIORITY.name());
|
||||
if (authorName != null && authorPriority != null) {
|
||||
author = new RaceLogEventAuthorImpl(authorName, authorPriority.intValue());
|
||||
} else {
|
||||
author = RaceLogEventAuthorImpl.createCompatibilityAuthor();
|
||||
}
|
||||
|
||||
String eventClass = (String) dbObject.get(FieldNames.RACE_LOG_EVENT_CLASS.name());
|
||||
if (eventClass.equals(RaceLogStartTimeEvent.class.getSimpleName())) {
|
||||
return loadRaceLogStartTimeEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogRaceStatusEvent.class.getSimpleName())) {
|
||||
return loadRaceLogRaceStatusEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogRaceStatusEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogFlagEvent.class.getSimpleName())) {
|
||||
return loadRaceLogFlagEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogFlagEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogPassChangeEvent.class.getSimpleName())) {
|
||||
return loadRaceLogPassChangeEvent(createdAt, timePoint, id, passId, competitors);
|
||||
return loadRaceLogPassChangeEvent(createdAt, author, logicalTimePoint, id, passId, competitors);
|
||||
} else if (eventClass.equals(RaceLogCourseAreaChangedEvent.class.getSimpleName())) {
|
||||
return loadRaceLogCourseAreaChangedEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogCourseAreaChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogCourseDesignChangedEvent.class.getSimpleName())) {
|
||||
return loadRaceLogCourseDesignChangedEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogCourseDesignChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogFinishPositioningListChangedEvent.class.getSimpleName())) {
|
||||
return loadRaceLogFinishPositioningListChangedEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogFinishPositioningListChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogFinishPositioningConfirmedEvent.class.getSimpleName())) {
|
||||
return loadRaceLogFinishPositioningConfirmedEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogFinishPositioningConfirmedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogPathfinderEvent.class.getSimpleName())) {
|
||||
return loadRaceLogPathfinderEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogPathfinderEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogGateLineOpeningTimeEvent.class.getSimpleName())) {
|
||||
return loadRaceLogGateLineOpeningTimeEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogGateLineOpeningTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogStartProcedureChangedEvent.class.getSimpleName())) {
|
||||
return loadRaceLogStartProcedureChangedEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogStartProcedureChangedEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogProtestStartTimeEvent.class.getSimpleName())) {
|
||||
return loadRaceLogRaceLogProtestStartTimeEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogRaceLogProtestStartTimeEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
} else if (eventClass.equals(RaceLogWindFixEvent.class.getSimpleName())) {
|
||||
return loadRaceLogRaceLogWindFixEvent(createdAt, timePoint, id, passId, competitors, dbObject);
|
||||
return loadRaceLogRaceLogWindFixEvent(createdAt, author, logicalTimePoint, id, passId, competitors, dbObject);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format("Unknown RaceLogEvent type %s", eventClass));
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogRaceLogWindFixEvent(TimePoint createdAt, TimePoint timePoint, Serializable id,
|
||||
Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogEvent loadRaceLogRaceLogWindFixEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
Wind wind = loadWind((DBObject) dbObject.get(FieldNames.WIND.name()));
|
||||
return raceLogEventFactory.createWindFixEvent(createdAt, timePoint, id, competitors, passId, wind);
|
||||
return raceLogEventFactory.createWindFixEvent(createdAt, author, logicalTimePoint, id, competitors, passId, wind);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogRaceLogProtestStartTimeEvent(TimePoint createdAt, TimePoint timePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogEvent loadRaceLogRaceLogProtestStartTimeEvent(TimePoint createdAt, RaceLogEventAuthor author,
|
||||
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
TimePoint protestStartTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_PROTEST_START_TIME);
|
||||
return raceLogEventFactory.createProtestStartTimeEvent(createdAt, timePoint, id, competitors, passId, protestStartTime);
|
||||
return raceLogEventFactory.createProtestStartTimeEvent(createdAt, author, logicalTimePoint, id, competitors, passId, protestStartTime);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogStartProcedureChangedEvent(TimePoint createdAt, TimePoint timePoint,
|
||||
private RaceLogEvent loadRaceLogStartProcedureChangedEvent(TimePoint createdAt, RaceLogEventAuthor author,
|
||||
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
RacingProcedureType type = RacingProcedureType.valueOf(dbObject.get(FieldNames.RACE_LOG_START_PROCEDURE_TYPE.name()).toString());
|
||||
return raceLogEventFactory.createStartProcedureChangedEvent(createdAt, author, logicalTimePoint, id, competitors, passId, type);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogGateLineOpeningTimeEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
StartProcedureType type = StartProcedureType.valueOf(dbObject.get(FieldNames.RACE_LOG_START_PROCEDURE_TYPE.name()).toString());
|
||||
return raceLogEventFactory.createStartProcedureChangedEvent(createdAt, timePoint, id, competitors, passId, type);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogGateLineOpeningTimeEvent(TimePoint createdAt, TimePoint timePoint, Serializable id,
|
||||
Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
Number gateLineOpeningTime = (Number) dbObject.get(FieldNames.RACE_LOG_GATE_LINE_OPENING_TIME.name());
|
||||
return raceLogEventFactory.createGateLineOpeningTimeEvent(createdAt, timePoint, id, competitors, passId,
|
||||
gateLineOpeningTime == null ? null : gateLineOpeningTime.longValue());
|
||||
Number gateLaunchStopTime = (Number) dbObject.get(FieldNames.RACE_LOG_GATE_LINE_OPENING_TIME.name());
|
||||
Number golfDownTime = 0;
|
||||
if (dbObject.containsField(FieldNames.RACE_LOG_GOLF_DOWN_TIME.name())) {
|
||||
golfDownTime = (Number) dbObject.get(FieldNames.RACE_LOG_GOLF_DOWN_TIME.name());
|
||||
}
|
||||
return raceLogEventFactory.createGateLineOpeningTimeEvent(createdAt, author, logicalTimePoint, id, competitors,
|
||||
passId, gateLaunchStopTime == null ? null : gateLaunchStopTime.longValue(), golfDownTime.longValue());
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogPathfinderEvent(TimePoint createdAt, TimePoint timePoint, Serializable id,
|
||||
Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogEvent loadRaceLogPathfinderEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
String pathfinderId = dbObject.get(FieldNames.RACE_LOG_PATHFINDER_ID.name()).toString();
|
||||
return raceLogEventFactory.createPathfinderEvent(createdAt, timePoint, id, competitors, passId, pathfinderId);
|
||||
return raceLogEventFactory.createPathfinderEvent(createdAt, author, logicalTimePoint, id, competitors, passId, pathfinderId);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogFinishPositioningConfirmedEvent(TimePoint createdAt, TimePoint timePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogEvent loadRaceLogFinishPositioningConfirmedEvent(TimePoint createdAt, RaceLogEventAuthor author,
|
||||
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
BasicDBList dbPositionedCompetitorList = (BasicDBList) dbObject.get(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name());
|
||||
List<Triple<Serializable, String, MaxPointsReason>> positionedCompetitors = null;
|
||||
CompetitorResults positionedCompetitors = null;
|
||||
//When a confirmation event is loaded that does not contain the positioned competitors (this is the case for the ESS events in
|
||||
//Singapore and Quingdao) then null should be set for the positionedCompetitors, which is evaluated later on.
|
||||
if (dbPositionedCompetitorList != null) {
|
||||
positionedCompetitors = loadPositionedCompetitors(dbPositionedCompetitorList);
|
||||
}
|
||||
|
||||
return raceLogEventFactory.createFinishPositioningConfirmedEvent(createdAt, timePoint, id, competitors, passId, positionedCompetitors);
|
||||
return raceLogEventFactory.createFinishPositioningConfirmedEvent(createdAt, author, logicalTimePoint, id, competitors, passId, positionedCompetitors);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogFinishPositioningListChangedEvent(TimePoint createdAt, TimePoint timePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogEvent loadRaceLogFinishPositioningListChangedEvent(TimePoint createdAt, RaceLogEventAuthor author,
|
||||
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
BasicDBList dbPositionedCompetitorList = (BasicDBList) dbObject.get(FieldNames.RACE_LOG_POSITIONED_COMPETITORS.name());
|
||||
List<Triple<Serializable, String, MaxPointsReason>> positionedCompetitors = loadPositionedCompetitors(dbPositionedCompetitorList);
|
||||
CompetitorResults positionedCompetitors = loadPositionedCompetitors(dbPositionedCompetitorList);
|
||||
|
||||
return raceLogEventFactory.createFinishPositioningListChangedEvent(createdAt, timePoint, id, competitors, passId, positionedCompetitors);
|
||||
return raceLogEventFactory.createFinishPositioningListChangedEvent(createdAt, author, logicalTimePoint, id, competitors, passId, positionedCompetitors);
|
||||
}
|
||||
|
||||
private RaceLogEvent loadRaceLogPassChangeEvent(TimePoint createdAt, TimePoint timePoint, Serializable id,
|
||||
Integer passId, List<Competitor> competitors) {
|
||||
return raceLogEventFactory.createPassChangeEvent(createdAt, timePoint, id, competitors, passId);
|
||||
private RaceLogEvent loadRaceLogPassChangeEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint,
|
||||
Serializable id, Integer passId, List<Competitor> competitors) {
|
||||
return raceLogEventFactory.createPassChangeEvent(createdAt, author, logicalTimePoint, id, competitors, passId);
|
||||
}
|
||||
|
||||
private RaceLogCourseDesignChangedEvent loadRaceLogCourseDesignChangedEvent(TimePoint createdAt, TimePoint timePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogCourseDesignChangedEvent loadRaceLogCourseDesignChangedEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
String courseName = (String) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN_NAME.name());
|
||||
CourseBase courseData = loadCourseData((BasicDBList) dbObject.get(FieldNames.RACE_LOG_COURSE_DESIGN.name()), courseName);
|
||||
return raceLogEventFactory.createCourseDesignChangedEvent(createdAt, timePoint, id, competitors, passId, courseData);
|
||||
return raceLogEventFactory.createCourseDesignChangedEvent(createdAt, author, logicalTimePoint, id, competitors, passId, courseData);
|
||||
}
|
||||
|
||||
private RaceLogCourseAreaChangedEvent loadRaceLogCourseAreaChangedEvent(TimePoint createdAt, TimePoint timePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogCourseAreaChangedEvent loadRaceLogCourseAreaChangedEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
Serializable courseAreaId = (Serializable) dbObject.get(FieldNames.COURSE_AREA_ID.name());
|
||||
return raceLogEventFactory.createCourseAreaChangedEvent(createdAt, timePoint, id, competitors, passId, courseAreaId);
|
||||
return raceLogEventFactory.createCourseAreaChangedEvent(createdAt, author, logicalTimePoint, id, competitors, passId, courseAreaId);
|
||||
}
|
||||
|
||||
private List<Triple<Serializable, String, MaxPointsReason>> loadPositionedCompetitors(BasicDBList dbPositionedCompetitorList) {
|
||||
List<Triple<Serializable, String, MaxPointsReason>> positionedCompetitors = new ArrayList<Triple<Serializable, String, MaxPointsReason>>();
|
||||
private CompetitorResults loadPositionedCompetitors(BasicDBList dbPositionedCompetitorList) {
|
||||
CompetitorResults positionedCompetitors = new CompetitorResultsImpl();
|
||||
for (Object object : dbPositionedCompetitorList) {
|
||||
DBObject dbObject = (DBObject) object;
|
||||
|
||||
@@ -1181,13 +1233,13 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
List<Competitor> competitors = new ArrayList<Competitor>();
|
||||
for (Object object : dbCompetitorList) {
|
||||
Serializable competitorId = (Serializable) object;
|
||||
Competitor competitor = baseDomainFactory.getExistingCompetitorById(competitorId);
|
||||
Competitor competitor = baseDomainFactory.getCompetitorStore().getExistingCompetitorById(competitorId);
|
||||
competitors.add(competitor);
|
||||
}
|
||||
return competitors;
|
||||
}
|
||||
|
||||
private RaceLogFlagEvent loadRaceLogFlagEvent(TimePoint createdAt, TimePoint timePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogFlagEvent loadRaceLogFlagEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
Flags upperFlag = Flags.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_FLAG_UPPER.name()));
|
||||
Flags lowerFlag = Flags.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_FLAG_LOWER.name()));
|
||||
Boolean displayed = Boolean.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_FLAG_DISPLAYED.name()));
|
||||
@@ -1196,17 +1248,17 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
return raceLogEventFactory.createFlagEvent(createdAt, timePoint, id, competitors, passId, upperFlag, lowerFlag, displayed);
|
||||
return raceLogEventFactory.createFlagEvent(createdAt, author, logicalTimePoint, id, competitors, passId, upperFlag, lowerFlag, displayed);
|
||||
}
|
||||
|
||||
private RaceLogStartTimeEvent loadRaceLogStartTimeEvent(TimePoint createdAt, TimePoint timePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogStartTimeEvent loadRaceLogStartTimeEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
TimePoint startTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_START_TIME);
|
||||
return raceLogEventFactory.createStartTimeEvent(createdAt, timePoint, id, competitors, passId, startTime);
|
||||
return raceLogEventFactory.createStartTimeEvent(createdAt, author, logicalTimePoint, id, competitors, passId, startTime);
|
||||
}
|
||||
|
||||
private RaceLogRaceStatusEvent loadRaceLogRaceStatusEvent(TimePoint createdAt, TimePoint timePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
private RaceLogRaceStatusEvent loadRaceLogRaceStatusEvent(TimePoint createdAt, RaceLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors, DBObject dbObject) {
|
||||
RaceLogRaceStatus nextStatus = RaceLogRaceStatus.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name()));
|
||||
return raceLogEventFactory.createRaceStatusEvent(createdAt, timePoint, id, competitors, passId, nextStatus);
|
||||
return raceLogEventFactory.createRaceStatusEvent(createdAt, author, logicalTimePoint, id, competitors, passId, nextStatus);
|
||||
}
|
||||
|
||||
private CourseBase loadCourseData(BasicDBList dbCourseList, String courseName) {
|
||||
@@ -1287,4 +1339,56 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Entry<DeviceConfigurationMatcher, DeviceConfiguration>> loadAllDeviceConfigurations() {
|
||||
Map<DeviceConfigurationMatcher, DeviceConfiguration> result = new HashMap<>();
|
||||
DBCollection configurationCollection = database.getCollection(CollectionNames.CONFIGURATIONS.name());
|
||||
|
||||
try {
|
||||
for (DBObject dbObject : configurationCollection.find()) {
|
||||
Pair<DeviceConfigurationMatcher, DeviceConfiguration> entry = loadConfigurationEntry(dbObject);
|
||||
result.put(entry.getA(), entry.getB());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load configurations.");
|
||||
logger.log(Level.SEVERE, "loadAllDeviceConfigurations", e);
|
||||
}
|
||||
|
||||
return result.entrySet();
|
||||
}
|
||||
|
||||
private Pair<DeviceConfigurationMatcher, DeviceConfiguration> loadConfigurationEntry(DBObject dbObject) {
|
||||
DBObject matcherObject = (DBObject) dbObject.get(FieldNames.CONFIGURATION_MATCHER.name());
|
||||
DBObject configObject = (DBObject) dbObject.get(FieldNames.CONFIGURATION_CONFIG.name());
|
||||
return new Pair<DeviceConfigurationMatcher, DeviceConfiguration>(loadConfigurationMatcher(matcherObject),
|
||||
loadConfiguration(configObject));
|
||||
}
|
||||
|
||||
private DeviceConfigurationMatcher loadConfigurationMatcher(DBObject matcherObject) {
|
||||
DeviceConfigurationMatcherType type = DeviceConfigurationMatcherType.valueOf(
|
||||
matcherObject.get(FieldNames.CONFIGURATION_MATCHER_TYPE.name()).toString());
|
||||
List<String> clientIdentifiers = new ArrayList<String>();
|
||||
BasicDBList clientIdentifiersObject = (BasicDBList) matcherObject.get(FieldNames.CONFIGURATION_MATCHER_CLIENTS.name());
|
||||
if (clientIdentifiersObject != null) {
|
||||
for (Object clientIdentifier : clientIdentifiersObject) {
|
||||
clientIdentifiers.add(clientIdentifier.toString());
|
||||
}
|
||||
}
|
||||
return baseDomainFactory.getOrCreateDeviceConfigurationMatcher(type, clientIdentifiers);
|
||||
}
|
||||
|
||||
private DeviceConfiguration loadConfiguration(DBObject configObject) {
|
||||
DeviceConfiguration configuration = null;
|
||||
try {
|
||||
JsonDeserializer<DeviceConfiguration> deserializer = DeviceConfigurationJsonDeserializer.create();
|
||||
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(configObject)));
|
||||
configuration = deserializer.deserialize(json);
|
||||
} catch (JsonDeserializationException | ParseException e) {
|
||||
logger.log(Level.SEVERE, "Error parsing configuration object from MongoDB, falling back to empty configuration.");
|
||||
logger.log(Level.SEVERE, "loadConfiguration", e);
|
||||
configuration = new DeviceConfigurationImpl(new RegattaConfigurationImpl());
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
+11
-3
@@ -18,6 +18,7 @@ public enum FieldNames {
|
||||
REGATTA_ID,
|
||||
REGATTA_BASE_NAME,
|
||||
REGATTA_SERIES,
|
||||
REGATTA_REGATTA_CONFIGURATION,
|
||||
SERIES_NAME,
|
||||
SERIES_IS_MEDAL,
|
||||
SERIES_STARTS_WITH_ZERO_SCORE,
|
||||
@@ -69,9 +70,16 @@ public enum FieldNames {
|
||||
|
||||
// race log
|
||||
RACE_LOG_IDENTIFIER,
|
||||
RACE_LOG_EVENT, RACE_LOG_EVENT_PASS_ID, RACE_LOG_EVENT_ID, RACE_LOG_EVENT_CREATED_AT, RACE_LOG_EVENT_INVOLVED_BOATS, RACE_LOG_EVENT_FLAG_UPPER, RACE_LOG_EVENT_FLAG_LOWER,
|
||||
RACE_LOG_EVENT, RACE_LOG_EVENT_PASS_ID, RACE_LOG_EVENT_ID, RACE_LOG_EVENT_CREATED_AT, RACE_LOG_EVENT_AUTHOR_NAME, RACE_LOG_EVENT_AUTHOR_PRIORITY,
|
||||
RACE_LOG_EVENT_INVOLVED_BOATS, RACE_LOG_EVENT_FLAG_UPPER, RACE_LOG_EVENT_FLAG_LOWER,
|
||||
RACE_LOG_EVENT_FLAG_DISPLAYED, RACE_LOG_EVENT_START_TIME, RACE_LOG_EVENT_CLASS, RACE_LOG_EVENT_NEXT_STATUS, RACE_LOG_COURSE_DESIGN, WAYPOINT_ID,
|
||||
WAYPOINT_NAME, WAYPOINT_PASSINGSIDE, CONTROLPOINT, CONTROLPOINT_CLASS, MARK_ID, MARK_COLOR, MARK_NAME, MARK_PATTERN, MARK_SHAPE, MARK_TYPE,
|
||||
CONTROLPOINT_VALUE, GATE_ID, GATE_NAME, GATE_LEFT, GATE_RIGHT, RACE_LOG_POSITIONED_COMPETITORS, RACE_LOG_PATHFINDER_ID, RACE_LOG_GATE_LINE_OPENING_TIME,
|
||||
RACE_LOG_START_PROCEDURE_TYPE, RACE_LOG_PROTEST_START_TIME, RACE_LOG_COURSE_DESIGN_NAME;
|
||||
CONTROLPOINT_VALUE, GATE_ID, GATE_NAME, GATE_LEFT, GATE_RIGHT, RACE_LOG_POSITIONED_COMPETITORS, RACE_LOG_PATHFINDER_ID, RACE_LOG_GATE_LINE_OPENING_TIME,
|
||||
RACE_LOG_GOLF_DOWN_TIME,
|
||||
RACE_LOG_START_PROCEDURE_TYPE, RACE_LOG_PROTEST_START_TIME, RACE_LOG_COURSE_DESIGN_NAME,
|
||||
|
||||
// configurations
|
||||
CONFIGURATION_MATCHER, CONFIGURATION_CONFIG, CONFIGURATION_MATCHER_ID, CONFIGURATION_MATCHER_TYPE, CONFIGURATION_MATCHER_CLIENTS,
|
||||
CONFIGURATION_CONFIG_ALLOWED_COURSE_AREA_NAMES, CONFIGURATION_CONFIG_MIN_ROUNDS, CONFIGURATION_CONFIG_MAX_ROUNDS, CONFIGURATION_CONFIG_RESULTS_MAIL,
|
||||
CONFIGURATION_CONFIG_PROCEDURES;
|
||||
}
|
||||
+76
-4
@@ -29,6 +29,11 @@ import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Timed;
|
||||
import com.sap.sailing.domain.base.Venue;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
|
||||
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
|
||||
import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationMatcherMulti;
|
||||
import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationMatcherSingle;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
@@ -39,6 +44,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
@@ -51,6 +57,7 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLogCourseAreaChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningConfirmedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningListChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
|
||||
@@ -68,7 +75,10 @@ import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.DeviceConfigurationJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.RegattaConfigurationJsonSerializer;
|
||||
|
||||
public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
private static Logger logger = Logger.getLogger(MongoObjectFactoryImpl.class.getName());
|
||||
@@ -467,6 +477,12 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
} else {
|
||||
dbRegatta.put(FieldNames.COURSE_AREA_ID.name(), null);
|
||||
}
|
||||
if (regatta.getRegattaConfiguration() != null) {
|
||||
JsonSerializer<RegattaConfiguration> serializer = RegattaConfigurationJsonSerializer.create();
|
||||
JSONObject json = serializer.serialize(regatta.getRegattaConfiguration());
|
||||
DBObject configurationObject = (DBObject) JSON.parse(json.toString());
|
||||
dbRegatta.put(FieldNames.REGATTA_REGATTA_CONFIGURATION.name(), configurationObject);
|
||||
}
|
||||
|
||||
regattasCollection.update(query, dbRegatta, /* upsrt */ true, /* multi */ false);
|
||||
}
|
||||
@@ -545,6 +561,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
result.ensureIndex(new BasicDBObject(FieldNames.RACE_LOG_IDENTIFIER.name(), null));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void storeRaceLogEventAuthor(DBObject dbObject, RaceLogEventAuthor author) {
|
||||
if (author != null) {
|
||||
dbObject.put(FieldNames.RACE_LOG_EVENT_AUTHOR_NAME.name(), author.getName());
|
||||
dbObject.put(FieldNames.RACE_LOG_EVENT_AUTHOR_PRIORITY.name(), author.getPriority());
|
||||
}
|
||||
}
|
||||
|
||||
public DBObject storeRaceLogEntry(RaceLogIdentifier raceLogIdentifier, RaceLogFlagEvent flagEvent) {
|
||||
BasicDBObject result = new BasicDBObject();
|
||||
@@ -702,11 +725,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
}
|
||||
|
||||
private void storeRaceLogEventProperties(RaceLogEvent event, DBObject result) {
|
||||
storeTimed(event, result);
|
||||
// for compatibility reasons we reuse the field name of Timed
|
||||
storeTimePoint(event.getLogicalTimePoint(), result, FieldNames.TIME_AS_MILLIS);
|
||||
storeTimePoint(event.getCreatedAt(), result, FieldNames.RACE_LOG_EVENT_CREATED_AT);
|
||||
result.put(FieldNames.RACE_LOG_EVENT_ID.name(), event.getId());
|
||||
result.put(FieldNames.RACE_LOG_EVENT_PASS_ID.name(), event.getPassId());
|
||||
result.put(FieldNames.RACE_LOG_EVENT_INVOLVED_BOATS.name(), storeInvolvedBoatsForRaceLogEvent(event.getInvolvedBoats()));
|
||||
storeRaceLogEventAuthor(result, event.getAuthor());
|
||||
}
|
||||
|
||||
|
||||
@@ -785,7 +810,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
|
||||
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogGateLineOpeningTimeEvent.class.getSimpleName());
|
||||
|
||||
result.put(FieldNames.RACE_LOG_GATE_LINE_OPENING_TIME.name(), gateLineOpeningTimeEvent.getGateLineOpeningTime());
|
||||
result.put(FieldNames.RACE_LOG_GATE_LINE_OPENING_TIME.name(), gateLineOpeningTimeEvent.getGateLineOpeningTimes().getGateLaunchStopTime());
|
||||
result.put(FieldNames.RACE_LOG_GOLF_DOWN_TIME.name(), gateLineOpeningTimeEvent.getGateLineOpeningTimes().getGolfDownTime());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -871,7 +897,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
public void storeCompetitor(Competitor competitor) {
|
||||
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
|
||||
JSONObject json = competitorSerializer.serialize(competitor);
|
||||
BasicDBObject query = new BasicDBObject(CompetitorJsonSerializer.FIELD_ID, competitor.getId());
|
||||
DBObject query = (DBObject) JSON.parse(competitorSerializer.getCompetitorIdQuery(competitor).toString());
|
||||
DBObject entry = (DBObject) JSON.parse(json.toString());
|
||||
collection.update(query, entry, /* upsrt */true, /* multi */false);
|
||||
}
|
||||
@@ -887,7 +913,53 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
public void removeCompetitor(Competitor competitor) {
|
||||
logger.info("Removing persistent competitor info for competitor "+competitor.getName()+" with ID "+competitor.getId());
|
||||
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
|
||||
BasicDBObject query = new BasicDBObject(CompetitorJsonSerializer.FIELD_ID, competitor.getId().toString());
|
||||
DBObject query = (DBObject) JSON.parse(competitorSerializer.getCompetitorIdQuery(competitor).toString());
|
||||
collection.remove(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeDeviceConfiguration(DeviceConfigurationMatcher matcher, DeviceConfiguration configuration) {
|
||||
DBCollection configurationsCollections = database.getCollection(CollectionNames.CONFIGURATIONS.name());
|
||||
|
||||
DBObject query = new BasicDBObject();
|
||||
query.put(FieldNames.CONFIGURATION_MATCHER_ID.name(), matcher.getMatcherIdentifier());
|
||||
|
||||
DBObject entryObject = new BasicDBObject();
|
||||
entryObject.put(FieldNames.CONFIGURATION_MATCHER_ID.name(), matcher.getMatcherIdentifier());
|
||||
entryObject.put(FieldNames.CONFIGURATION_MATCHER.name(), createDeviceConfigurationMatcherObject(matcher));
|
||||
entryObject.put(FieldNames.CONFIGURATION_CONFIG.name(), createDeviceConfigurationObject(configuration));
|
||||
|
||||
configurationsCollections.update(query, entryObject, /* upsrt */ true, /* multi */ false);
|
||||
}
|
||||
|
||||
private DBObject createDeviceConfigurationMatcherObject(DeviceConfigurationMatcher matcher) {
|
||||
DBObject matcherObject = new BasicDBObject();
|
||||
matcherObject.put(FieldNames.CONFIGURATION_MATCHER_TYPE.name(), matcher.getMatcherType().name());
|
||||
if (matcher instanceof DeviceConfigurationMatcherSingle) {
|
||||
BasicDBList client = new BasicDBList();
|
||||
client.add(((DeviceConfigurationMatcherSingle)matcher).getClientIdentifier());
|
||||
matcherObject.put(FieldNames.CONFIGURATION_MATCHER_CLIENTS.name(), client);
|
||||
} else if (matcher instanceof DeviceConfigurationMatcherMulti) {
|
||||
BasicDBList clients = new BasicDBList();
|
||||
Util.addAll(((DeviceConfigurationMatcherMulti)matcher).getClientIdentifiers(), clients);
|
||||
matcherObject.put(FieldNames.CONFIGURATION_MATCHER_CLIENTS.name(), clients);
|
||||
}
|
||||
return matcherObject;
|
||||
}
|
||||
|
||||
private DBObject createDeviceConfigurationObject(DeviceConfiguration configuration) {
|
||||
JsonSerializer<DeviceConfiguration> serializer = DeviceConfigurationJsonSerializer.create();
|
||||
JSONObject json = serializer.serialize(configuration);
|
||||
DBObject entry = (DBObject) JSON.parse(json.toString());
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDeviceConfiguration(DeviceConfigurationMatcher matcher) {
|
||||
DBCollection configurationsCollections = database.getCollection(CollectionNames.CONFIGURATIONS.name());
|
||||
DBObject query = new BasicDBObject();
|
||||
query.put(FieldNames.CONFIGURATION_MATCHER_ID.name(), matcher.getMatcherIdentifier());
|
||||
configurationsCollections.remove(query);
|
||||
}
|
||||
|
||||
}
|
||||
+10
-8
@@ -15,25 +15,27 @@ public class MongoRaceLogStoreImpl implements RaceLogStore {
|
||||
private final MongoObjectFactory mongoObjectFactory;
|
||||
private final DomainObjectFactory domainObjectFactory;
|
||||
private final Map<RaceLogIdentifier, RaceLog> raceLogCache;
|
||||
private final WeakHashMap<RaceLog, MongoRaceLogListener> listeners;
|
||||
private final WeakHashMap<RaceLog, MongoRaceLogStoreVisitor> listeners;
|
||||
|
||||
public MongoRaceLogStoreImpl(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory) {
|
||||
this.mongoObjectFactory = mongoObjectFactory;
|
||||
this.domainObjectFactory = domainObjectFactory;
|
||||
this.raceLogCache = new HashMap<>();
|
||||
this.listeners = new WeakHashMap<RaceLog, MongoRaceLogListener>();
|
||||
this.listeners = new WeakHashMap<RaceLog, MongoRaceLogStoreVisitor>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceLog getRaceLog(RaceLogIdentifier identifier, boolean ignoreCache) {
|
||||
final RaceLog result;
|
||||
if (!ignoreCache && raceLogCache.containsKey(identifier)) {
|
||||
return raceLogCache.get(identifier);
|
||||
result = raceLogCache.get(identifier);
|
||||
} else {
|
||||
result = domainObjectFactory.loadRaceLog(identifier);
|
||||
MongoRaceLogStoreVisitor listener = new MongoRaceLogStoreVisitor(identifier, mongoObjectFactory);
|
||||
listeners.put(result, listener);
|
||||
result.addListener(listener);
|
||||
raceLogCache.put(identifier, result);
|
||||
}
|
||||
RaceLog result = domainObjectFactory.loadRaceLog(identifier);
|
||||
MongoRaceLogListener listener = new MongoRaceLogListener(identifier, mongoObjectFactory);
|
||||
listeners.put(result, listener);
|
||||
result.addListener(listener);
|
||||
raceLogCache.put(identifier, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -21,15 +21,15 @@ import com.sap.sailing.domain.racelog.RaceLogStartProcedureChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogWindFixEvent;
|
||||
|
||||
public class MongoRaceLogListener implements RaceLogEventVisitor {
|
||||
public class MongoRaceLogStoreVisitor implements RaceLogEventVisitor {
|
||||
|
||||
private final RaceLogIdentifier raceLogIdentifier;
|
||||
private final MongoObjectFactoryImpl mongoObjectFactory;
|
||||
private final DBCollection raceLogsCollection;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(MongoRaceLogListener.class.getName());
|
||||
private final static Logger logger = Logger.getLogger(MongoRaceLogStoreVisitor.class.getName());
|
||||
|
||||
public MongoRaceLogListener(RaceLogIdentifier identifier, MongoObjectFactory mongoObjectFactory) {
|
||||
public MongoRaceLogStoreVisitor(RaceLogIdentifier identifier, MongoObjectFactory mongoObjectFactory) {
|
||||
super();
|
||||
this.raceLogIdentifier = identifier;
|
||||
this.mongoObjectFactory = (MongoObjectFactoryImpl) mongoObjectFactory;
|
||||
+9
-12
@@ -5,21 +5,19 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.impl.Util.Triple;
|
||||
import com.sap.sailing.domain.racelog.CompetitorResults;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningConfirmedEvent;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.ConfirmedFinishPositioningListFinder;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ConfirmedFinishPositioningListFinderTest extends
|
||||
PassAwareRaceLogAnalyzerTest<ConfirmedFinishPositioningListFinder, List<Triple<Serializable, String, MaxPointsReason>>> {
|
||||
PassAwareRaceLogAnalyzerTest<ConfirmedFinishPositioningListFinder, CompetitorResults> {
|
||||
|
||||
@Override
|
||||
protected ConfirmedFinishPositioningListFinder createAnalyzer(RaceLog raceLog) {
|
||||
@@ -27,11 +25,10 @@ public class ConfirmedFinishPositioningListFinderTest extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Triple<Serializable, String, MaxPointsReason>> setupTargetEventsForPassAwareTests(int passId) {
|
||||
RaceLogFinishPositioningConfirmedEvent event = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1, passId);
|
||||
when(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(mock(List.class));
|
||||
raceLog.add(event);
|
||||
return event.getPositionedCompetitorsIDsNamesMaxPointsReasons();
|
||||
protected TargetPair getTargetEventsAndResultForPassAwareTests(int passId, RaceLogEventAuthor author) {
|
||||
RaceLogFinishPositioningConfirmedEvent event = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1, passId, author);
|
||||
when(event.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(mock(CompetitorResults.class));
|
||||
return new TargetPair(Arrays.asList(event), event.getPositionedCompetitorsIDsNamesMaxPointsReasons());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,7 +42,7 @@ public class ConfirmedFinishPositioningListFinderTest extends
|
||||
public void testMostRecent() {
|
||||
RaceLogFinishPositioningConfirmedEvent event1 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1);
|
||||
RaceLogFinishPositioningConfirmedEvent event2 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 2);
|
||||
when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(mock(List.class));
|
||||
when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(mock(CompetitorResults.class));
|
||||
|
||||
raceLog.add(event1);
|
||||
raceLog.add(event2);
|
||||
|
||||
+9
-7
@@ -4,12 +4,15 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
import com.sap.sailing.domain.racelog.RaceLogRaceStatusEvent;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.FinishedTimeFinder;
|
||||
|
||||
@@ -19,13 +22,12 @@ public class FinishedTimeFinderTest extends PassAwareRaceLogAnalyzerTest<Finishe
|
||||
protected FinishedTimeFinder createAnalyzer(RaceLog raceLog) {
|
||||
return new FinishedTimeFinder(raceLog);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TimePoint setupTargetEventsForPassAwareTests(int passId) {
|
||||
RaceLogRaceStatusEvent event = createEvent(RaceLogRaceStatusEvent.class, 1, passId);
|
||||
protected TargetPair getTargetEventsAndResultForPassAwareTests(int passId, RaceLogEventAuthor author) {
|
||||
RaceLogRaceStatusEvent event = createEvent(RaceLogRaceStatusEvent.class, 1, passId, author);
|
||||
when(event.getNextStatus()).thenReturn(RaceLogRaceStatus.FINISHED);
|
||||
raceLog.add(event);
|
||||
return event.getTimePoint();
|
||||
return new TargetPair(Arrays.asList(event), event.getLogicalTimePoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,7 +47,7 @@ public class FinishedTimeFinderTest extends PassAwareRaceLogAnalyzerTest<Finishe
|
||||
raceLog.add(event1);
|
||||
raceLog.add(event2);
|
||||
|
||||
assertEquals(event2.getTimePoint(), analyzer.analyze());
|
||||
assertEquals(event2.getLogicalTimePoint(), analyzer.analyze());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -58,6 +60,6 @@ public class FinishedTimeFinderTest extends PassAwareRaceLogAnalyzerTest<Finishe
|
||||
raceLog.add(event1);
|
||||
raceLog.add(event2);
|
||||
|
||||
assertEquals(event1.getTimePoint(), analyzer.analyze());
|
||||
assertEquals(event1.getLogicalTimePoint(), analyzer.analyze());
|
||||
}
|
||||
}
|
||||
Loaded 100 of 987 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user