Merge branch 'master' into bug5060

This commit is contained in:
Axel Uhl
2020-03-08 00:39:59 +01:00
935 changed files with 32334 additions and 3629 deletions
@@ -0,0 +1,119 @@
package com.sap.sailing.domain.common;
import java.util.HashMap;
import java.util.Map;
/**
* Given a string that is assumed to contain a "sail number" or "sail ID" this class offers methods to canonicalize and
* parse that string in order to make matches across different systems that handle such sail numbers / IDs. For example,
* the canonicalization process ignores case and whitespace, and offers to use a default nationality when comparing in
* case a sail number doesn't have a nationality.
* <p>
*
* Since the logic is intended to work for both, browser GWT client as well as OSGi Java back-end, and because GWT does
* not have a regular expression library that is compatible with the Java default regexp library, the regular expression
* constant needs to be turned into a regular expression matcher in a "platform-specific" way, making this class
* abstract.
*
* @param <CompetitorType>
* the type of competitor; different for GWT client where it will be the {@code CompetitorDTO}, and back-end
* where it will be the {@link Competitor}, leading to specific method implementations for extracting the
* sail number
*
* @author Axel Uhl (D043530)
*
*/
public abstract class AbstractSailNumberCanonicalizerAndMatcher<CompetitorType> {
protected static final String sailIdRegexpPattern = "^([A-Z][A-Z][A-Z])\\s*[^0-9]*([0-9]*)$";
public static class SailNumberMatch {
private final String iocCode;
private final String number;
public SailNumberMatch(String iocCode, String number) {
super();
this.iocCode = iocCode;
this.number = number;
}
public String getIocCode() {
return iocCode;
}
public String getNumber() {
return number;
}
}
/**
* Parses a sail number into a nationality code expected at the beginning of the string,
* and a numeric part assumed to be at the end of the string. Before matching, the {@code sailId}
* string will be {@link String#trim() trimmed}, removing leading and trailing whitespace. If
* the parameter matches the {@link #sailIdRegexpPattern} then a valid {@link SailNumberMatch}
* object is returned which may, however, deliver empty or {@code null} parts for the
* {@link SailNumberMatch#getIocCode() IOC code} or the {@link SailNumberMatch#getNumber() number}.
* If the string is not matched, {@code null} is returned.
*/
abstract protected SailNumberMatch match(String sailId);
/**
* Try to match three-letter country code and number, optionally separated by whitespaces. If there is no match,
* use the first 20 characters of the sailID.
*/
public String canonicalizeSailID(String sailID, String defaultNationality) {
String result = null;
SailNumberMatch m = match(sailID);
if (m != null) {
String iocCode = m.getIocCode();
if (iocCode != null) {
iocCode = iocCode.toUpperCase();
}
if (defaultNationality != null && (iocCode == null || iocCode.trim().length() == 0)) {
iocCode = defaultNationality.toUpperCase();
}
if (iocCode != null && iocCode.trim().length() > 0) {
String number = m.getNumber();
result = iocCode + number;
}
}
if (result == null) {
result = sailID.substring(0, Math.min(20, sailID.length()));
}
return result;
}
/**
* Maps the sail IDs contained in {@code sailNumbers} to the {@code competitors} passed. The match making ignores
* all whitespaces in the sail IDs on both sides (see {@link #canonicalizeSailID(String, String)}). If a
* competitor's sail number does not start with a letter it is assumed the country code is missing. In this case,
* {@link #getThreeLetterIocCountryCode(Object) it} is prepended before comparing to the sail ID from
* {@code sailNumbers}. The sail ID number is extracted by trimming and using all trailing digits.
* @return
*
* @return a map mapping the sailIDs as found in {@code sailNumbers} to the {@code competitors}; values may be
* {@code null} if no matching competitor was found for the sail ID in the {@code competitors} collection
*/
public Map<String, CompetitorType> mapCompetitorsAndInitializeAllOfficialRaceIDs(final Iterable<CompetitorType> competitors, Iterable<String> sailNumbers) {
final Map<String, CompetitorType> result = new HashMap<>();
final Map<String, CompetitorType> canonicalizedSailIDToCompetitors = canonicalizeLeaderboardSailIDs(competitors);
for (final String sailNumber : sailNumbers) {
final String canonicalizedSailNumber = canonicalizeSailID(sailNumber, /* defaultNationality */ null);
final CompetitorType competitor = canonicalizedSailIDToCompetitors.get(canonicalizedSailNumber);
result.put(sailNumber, competitor);
}
return result;
}
public Map<String, CompetitorType> canonicalizeLeaderboardSailIDs(final Iterable<CompetitorType> competitors) {
Map<String, CompetitorType> result = new HashMap<>();
for (final CompetitorType competitor : competitors) {
final String competitorIdentifyingText = getCompetitorIdentifyingText(competitor);
String canonicalizedSailID = canonicalizeSailID(competitorIdentifyingText.trim(), getThreeLetterIocCountryCode(competitor).trim());
if (canonicalizedSailID != null) {
result.put(canonicalizedSailID, competitor);
}
}
return result;
}
abstract protected String getThreeLetterIocCountryCode(CompetitorType competitor);
abstract protected String getCompetitorIdentifyingText(CompetitorType competitor);
}
@@ -1,14 +1,17 @@
package com.sap.sailing.domain.common;
public class BranchIOConstants {
public static final String SAILINSIGHT_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/invite";
public static final String SAILINSIGHT_APP_BRANCHIO = "https://sailinsight-app.sapsailing.com/invite";
public static final String SAILINSIGHT_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/invite";
public static final String SAILINSIGHT_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/invite";
public static final String SAILINSIGHT_APP_BRANCHIO_PATH = "checkinUrl";
public static final String BUOYPINGER_APP_BRANCHIO = "https://buoypinger-app.sapsailing.com/invite";
public static final String BUOYPINGER_APP_BRANCHIO_PATH = "checkinUrl";
public static final String OPEN_REGATTA_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/publicInvite";
public static final String OPEN_REGATTA_2_APP_BRANCHIO_PATH = "checkinUrl";
public static final String OPEN_REGATTA_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/publicInvite";
public static final String OPEN_REGATTA_3_APP_BRANCHIO_PATH = "checkinUrl";
private BranchIOConstants() {
};
}
}
@@ -100,6 +100,9 @@ public enum DetailType implements Serializable {
OVERALL_TOTAL_DURATION_FOILED_IN_SECONDS(0, DESCENDING, "TOTAL_DURATION_FOILED_IN_SECONDS"),
OVERALL_TOTAL_DISTANCE_FOILED_IN_METERS(0, DESCENDING, "TOTAL_DISTANCE_FOILED_IN_METERS"),
RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
RACE_CURRENT_COURSE_OVER_GROUND_IN_TRUE_DEGREES(1, ASCENDING),
RACE_CURRENT_POSITION_LAT_DEG(10, ASCENDING),
RACE_CURRENT_POSITION_LNG_DEG(10, ASCENDING),
BRAVO_RACE_CURRENT_RIDE_HEIGHT_IN_METERS(2, DESCENDING),
RACE_CURRENT_DISTANCE_FOILED_IN_METERS(0, DESCENDING),
RACE_CURRENT_DURATION_FOILED_IN_SECONDS(0, DESCENDING),
@@ -110,6 +113,7 @@ public enum DetailType implements Serializable {
RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING),
RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING),
RACE_CALCULATED_TIME_TRAVELED(0, ASCENDING),
RACE_IMPLIED_WIND(5, DESCENDING),
RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(0, ASCENDING),
RACE_TIME_TRAVELED(0, ASCENDING),
RACE_TIME_TRAVELED_UPWIND(0, ASCENDING),
@@ -259,6 +263,7 @@ public enum DetailType implements Serializable {
availableDetailsTypes.add(DetailType.LEG_GAP_TO_LEADER_IN_SECONDS);
availableDetailsTypes.add(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
availableDetailsTypes.add(DetailType.RACE_RANK);
availableDetailsTypes.add(DetailType.RACE_IMPLIED_WIND);
availableDetailsTypes.add(DetailType.REGATTA_RANK);
availableDetailsTypes.add(DetailType.CHART_DISTANCE_TO_START_LINE);
availableDetailsTypes.add(DetailType.CHART_BEAT_ANGLE);
@@ -365,6 +370,7 @@ public enum DetailType implements Serializable {
allowed.add(RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START);
allowed.add(RACE_TIME_TRAVELED);
allowed.add(RACE_CALCULATED_TIME_TRAVELED);
allowed.add(RACE_IMPLIED_WIND);
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
allowed.add(RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
allowed.add(RACE_CURRENT_DISTANCE_FOILED_IN_METERS);
@@ -406,6 +412,7 @@ public enum DetailType implements Serializable {
allowed.add(OVERALL_TOTAL_AVERAGE_SPEED_OVER_GROUND);
allowed.add(OVERALL_TOTAL_TIME_SAILED_IN_SECONDS);
allowed.add(OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS);
// TODO bug5209 make ToT / ToD details depend on ranking metric
allowed.add(OVERALL_TIME_ON_TIME_FACTOR);
allowed.add(OVERALL_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE);
allowed.add(OVERALL_TOTAL_SCORED_RACE_COUNT);
@@ -422,6 +429,21 @@ public enum DetailType implements Serializable {
return allowed;
}
public static Collection<? extends DetailType> getAllToTToDHandicapDetailTypes() {
final Collection<DetailType> allowed = new LinkedHashSet<>();
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
allowed.add(RACE_CALCULATED_TIME_TRAVELED);
return allowed;
}
public static Collection<? extends DetailType> getAllOrcPerformanceCurveDetailTypes() {
final Collection<DetailType> allowed = new LinkedHashSet<>();
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
allowed.add(RACE_CALCULATED_TIME_TRAVELED);
allowed.add(RACE_IMPLIED_WIND);
return allowed;
}
public static Collection<DetailType> getLegDetailColumnTypes() {
final Collection<DetailType> allowed = new LinkedHashSet<>();
allowed.add(LEG_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS);
@@ -571,5 +593,4 @@ public enum DetailType implements Serializable {
}
throw new IllegalArgumentException("Could not restore " + value + " to an DetailType enum");
}
}
@@ -41,16 +41,15 @@ import java.io.Serializable;
*/
public interface DeviceIdentifier extends Serializable {
/**
* The returned {@link String} is used to look up corresponding services for serialization
* and persistence.
* The returned {@link String} is used to look up corresponding services for serialization and persistence.
*
* The reason for this design choice is that in future, third parties could easily write their own adapter and only
* have to register new OSGi service, and not touch the SAP Sailing Analytics code.
* The reason for this design choice is that in the future third parties could easily write their own adapter and
* only have to register a new OSGi service, and not touch the SAP Sailing Analytics code.
*/
String getIdentifierType();
/**
* Create a string representation, that can identify this device.
* Create a string representation that can identify this device.
* The returned values should be unique for this identifier within its {@link #getIdentifierType() type},
* but need not include the {@link #getIdentifierType() type} itself in the representation.
*/
@@ -1,9 +1,50 @@
package com.sap.sailing.domain.common;
public enum MailInvitationType {
LEGACY,
SailInsight1,
SailInsight2;
// Legacy invitations do not need any Branch.io information because
// links are constructed by different implementation.
LEGACY(/* supportsOpenRegattas */ false,
/* branchIOinviteURL */ null,
/* branchIOcheckinPath */ null,
/* branchIOopenRegattaURL */ null),
SailInsight1(/* supportsOpenRegattas */ false,
/* branchIOinviteURL */ BranchIOConstants.SAILINSIGHT_APP_BRANCHIO,
/* branchIOcheckinPath */ BranchIOConstants.SAILINSIGHT_APP_BRANCHIO_PATH,
/* branchIOopenRegattaURL */ null),
SailInsight2(/* supportsOpenRegattas */ true,
/* branchIOinviteURL */ BranchIOConstants.SAILINSIGHT_2_APP_BRANCHIO,
/* branchIOcheckinPath */ BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO_PATH,
/* branchIOopenRegattaURL */ BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO),
SailInsight3(/* supportsOpenRegattas */ true,
/* branchIOinviteURL */ BranchIOConstants.SAILINSIGHT_3_APP_BRANCHIO,
/* branchIOcheckinPath */ BranchIOConstants.OPEN_REGATTA_3_APP_BRANCHIO_PATH,
/* branchIOopenRegattaURL */ BranchIOConstants.OPEN_REGATTA_3_APP_BRANCHIO);
public static final String SYSTEM_PROPERTY_NAME = "com.sap.sailing.domain.tracking.MailInvitationType";
private MailInvitationType(boolean supportsOpenRegattas, String branchIOinviteURL, String branchIOcheckinPath,
String branchIOopenRegattaURL) {
this.supportsOpenRegattas = supportsOpenRegattas;
this.branchIOinviteURL = branchIOinviteURL;
this.branchIOcheckinPath = branchIOcheckinPath;
this.branchIOopenRegattaURL = branchIOopenRegattaURL;
}
public boolean isSupportsOpenRegattas() {
return supportsOpenRegattas;
}
public String getBranchIOinviteURL() {
return branchIOinviteURL;
}
public String getBranchIOcheckinPath() {
return branchIOcheckinPath;
}
public String getBranchIOopenRegattaURL() {
return branchIOopenRegattaURL;
}
private final boolean supportsOpenRegattas;
private final String branchIOinviteURL;
private final String branchIOcheckinPath;
private final String branchIOopenRegattaURL;
}
@@ -63,7 +63,7 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
@Override
@@ -76,7 +76,7 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
}
@Override
public HasPermissions getType() {
public HasPermissions getPermissionType() {
return SecuredDomainType.TRACKED_RACE;
}
@@ -15,6 +15,10 @@ public interface RegattaScoreCorrections {
public interface ScoreCorrectionsForRace {
String getRaceNameOrNumber();
/**
* Matched against the sail ID/number if the competitor is assigned a boat throughout the leaderboard,
* or against the competitor's short name otherwise.
*/
Set<String> getSailIDs();
ScoreCorrectionForCompetitorInRace getScoreCorrectionForCompetitor(String sailID);
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.common;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;
@@ -22,7 +23,7 @@ public interface ScoreCorrectionProvider extends Named {
* score corrections taken at different times. Later score corrections are expected to be cumulative, meaning they
* also contain all previous corrections.
*/
public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws Exception;
Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws Exception;
/**
* @param eventName
@@ -35,5 +36,10 @@ public interface ScoreCorrectionProvider extends Named {
* a time point as returned in the {@link Pair#getB()} component of the values in the map returned by
* {@link #getHasResultsForBoatClassFromDateByEventName()}.
*/
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName, TimePoint timePoint) throws Exception;
RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName, TimePoint timePoint) throws Exception;
/**
* Produces a single {@link RegattaScoreCorrections} object from a single {@link InputStream}.
*/
RegattaScoreCorrections getScoreCorrections(InputStream inputStream) throws Exception;
}
@@ -122,13 +122,13 @@ public class BoatDTO extends NamedSecuredObjectDTO implements WithID, Serializab
}
@Override
public HasPermissions getType() {
public HasPermissions getPermissionType() {
return SecuredDomainType.BOAT;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
@@ -73,4 +73,10 @@ public interface CompetitorDTO extends Named, WithID, SecuredDTO, Serializable,
* from the previous version of the enclosing leaderboard.
*/
CompetitorDTO getCompetitorFromPrevious(LeaderboardDTO previousVersion);
/**
* Erases the fields a user is not supposed to read with only the READ_PUBLIC and not the
* READ permission.
*/
void clearNonPublicFields();
}
@@ -247,17 +247,22 @@ public class CompetitorDTOImpl extends NamedSecuredObjectDTO implements Competit
}
@Override
public HasPermissions getType() {
public HasPermissions getPermissionType() {
return SecuredDomainType.COMPETITOR;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
return new TypeRelativeObjectIdentifier(idAsString);
}
@Override
public void clearNonPublicFields() {
email = null;
}
}
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
/**
@@ -121,6 +122,19 @@ public class LeaderboardEntryDTO implements Serializable {
*/
public Duration calculatedTime;
/**
* If this is a leaderboard entry for an ORC Performance Curve Scoring (PCS) race,
* the field holds the so-called "implied wind" as a speed. It is the wind speed with which,
* according to its polar, the competitor would have sailed the part of the course sailed
* so far in the time elapsed so far. For ORC PCS before 2015 this was the primary ranking
* criterion. Since 2015, however, implied wind has lost in significance because it is
* used only to compute a default wind speed by maximizing all implied wind values of all
* competitors in the race, and then using this wind speed to determine the time allowance
* for each competitor so that a different between time elapsed and the allowance can then
* be used for ranking.
*/
public Speed impliedWind;
/**
* The corrections applied to the time and distance sailed when the competitor would have reached the
* competitor farthest ahead, based on average VMG on the current leg and equal performance to the boat
@@ -150,6 +150,12 @@ public class PreviousCompetitorDTOImpl extends NamedSecuredObjectDTO implements
" need to be replaced by an object of "+CompetitorWithBoatDTOImpl.class.getName()+" after deserialization");
}
@Override
public void clearNonPublicFields() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
" need to be replaced by an object of "+CompetitorWithBoatDTOImpl.class.getName()+" after deserialization");
}
@Override
public Double getTimeOnTimeFactor() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
@@ -174,13 +180,13 @@ public class PreviousCompetitorDTOImpl extends NamedSecuredObjectDTO implements
}
@Override
public HasPermissions getType() {
public HasPermissions getPermissionType() {
return SecuredDomainType.COMPETITOR;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
@@ -129,13 +129,13 @@ public class RaceDTO extends BasicRaceDTO implements SecuredDTO {
}
@Override
public HasPermissions getType() {
public HasPermissions getPermissionType() {
return SecuredDomainType.TRACKED_RACE;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
@@ -171,11 +171,11 @@ public class MediaTrack implements Serializable, WithQualifiedObjectIdentifier {
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
}
@Override
public HasPermissions getType() {
public HasPermissions getPermissionType() {
return SecuredDomainType.MEDIA_TRACK;
}
@@ -3,33 +3,67 @@ package com.sap.sailing.domain.common.orc;
import java.io.Serializable;
import java.util.Map;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.DegreeBearingImpl;
/**
* Represents semantically a real ORC certificate for a {@link Competitor}, which is used to rate different type of
* boats for different insohre and offshore race conditions.
* boats for different inshore and offshore race conditions.
* <p>
* An ORC certificate is issued by the "Member National Authorities" of World Sailing and are available for insight at
* https://www.orc.org/index.asp . Other information about the whole scoring system and different variants are available
* too.
* <p>
* One implementing class provides all necessary functionalities to score the Competitors with a choosen
* {@link RankingMetric}.<p>
* {@link RankingMetric}.
* <p>
*
* The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that
* contains the concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo}
* and the {@code BIN} fields, as provided as single fields in the JSON representation, and as provided
* in the concatenated form in the {@code NATCERTN.FILE_ID} column in the RMS format.
* The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that contains the
* concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo} and the {@code BIN}
* fields, as provided as single fields in the JSON representation, and as provided in the concatenated form in the
* {@code NATCERTN.FILE_ID} column in the RMS format.
* <p>
*
* The certificate contains time allowances (or, conversely, speed predictions) for a combination of true wind speeds
* (TWS) and true wind angles (TWA). There are defaults for the values used for the true wind speeds and the true wind
* angles, forming a regular, fully populated matrix of allowances / speed predictions. These defaults can be found in
* {@link #ALLOWANCES_TRUE_WIND_SPEEDS} and {@link #ALLOWANCES_TRUE_WIND_SPEEDS}.
* <p>
*
* Certificates may also specify non-default values for the slots or "bins" of the matrix that describes the allowances
* / speed predictions. As for the default values, the allowances matrix has to be fully populated. The values in the
* TWS and TWA arrays are expected to be increasing monotonously.
*
* @author Daniel Lisunkin (i505543)
*
*/
public interface ORCCertificate extends WithID, Serializable {
/**
* Equals the column heading of the allowances table of an ORC certificate. The speeds are set by the offshore
* racing congress. The speeds occur in the array in ascending order.
*
* There are references in the persistance module. If the values change, there will be an adjustment needed
* in {@link MongoObjectFactoryImpl.speedToKnotsString}.
*/
Speed[] ALLOWANCES_TRUE_WIND_SPEEDS = { new KnotSpeedImpl(6), new KnotSpeedImpl(8),
new KnotSpeedImpl(10), new KnotSpeedImpl(12), new KnotSpeedImpl(14), new KnotSpeedImpl(16),
new KnotSpeedImpl(20) };
/**
* Equals the line heading of the allowances table of an ORC certificate. The true wind angles are set by the
* offshore racing congress. The angles occur in the array in ascending order.
*
* There are references in the persistance module. If the values change, there will be an adjustment needed
* in {@link MongoObjectFactoryImpl.bearingToDegreeString}.
*/
Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60),
new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110),
new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) };
/**
* As the ID of an ORC certificate we use the concatenation (without intermediate white space) of the
* {@code NatAuth}, the {@code CertNo} and the {@code BIN} fields, as provided as single fields in the JSON
@@ -128,4 +162,19 @@ public interface ORCCertificate extends WithID, Serializable {
Map<Speed, Map<Bearing, Speed>> getVelocityPredictionPerTrueWindSpeedAndAngle();
String getBoatName();
/**
* @return the true wind speeds used for the matrix returned by
* {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. The {@link Speed} values returned by this
* method form the key set of the map returned by {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}.
*/
Speed[] getTrueWindSpeeds();
/**
* @return the true wind angles used for the matrix returned by
* {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. The {@link Bearing} values returned by this
* method form the key set of all the maps returned as values in the map returned by
* {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}.
*/
Bearing[] getTrueWindAngles();
}
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.common.orc.impl;
import java.util.Collections;
import java.util.Map;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.orc.ORCCertificate;
import com.sap.sse.common.Bearing;
@@ -12,7 +11,6 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.Speed;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.DegreeBearingImpl;
/**
* Holds an ORC boat certificate including various of the metrics found in it. Note that multiple different certificates
@@ -28,28 +26,6 @@ public class ORCCertificateImpl implements ORCCertificate {
private final String idConsistingOfNatAuthCertNoAndBIN;
/**
* Equals the column heading of the allowances table of an ORC certificate. The speeds are set by the offshore
* racing congress. The speeds occur in the array in ascending order.
*
* There are references in the persistance module. If the values change, there will be an adjustment needed
* in {@link MongoObjectFactoryImpl.speedToKnotsString}.
*/
public static final Speed[] ALLOWANCES_TRUE_WIND_SPEEDS = { new KnotSpeedImpl(6), new KnotSpeedImpl(8),
new KnotSpeedImpl(10), new KnotSpeedImpl(12), new KnotSpeedImpl(14), new KnotSpeedImpl(16),
new KnotSpeedImpl(20) };
/**
* Equals the line heading of the allowances table of an ORC certificate. The true wind angles are set by the
* offshore racing congress. The angles occur in the array in ascending order.
*
* There are references in the persistance module. If the values change, there will be an adjustment needed
* in {@link MongoObjectFactoryImpl.bearingToDegreeString}.
*/
public static final Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60),
new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110),
new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) };
public final static Distance NAUTICAL_MILE = new NauticalMileDistance(1);
private final String sailNumber;
@@ -60,7 +36,16 @@ public class ORCCertificateImpl implements ORCCertificate {
private final Double cdl;
private final TimePoint issueDate;
// TODO add meaningful Javadoc
/**
* The "core" of the certificate for performance curve scoring; the values in the map tell how fast the boat
* described by this certificate is expected to sail for the true wind speed (TWS) provided by the key, and the true
* wind angle (TWA) provided by the value map's key. This is the reciprocal of the duration it takes the boat to
* sail a certain distance.
* <p>
*
* The keys are the speeds as provided by {@link #allowancesTrueWindSpeeds}; the keys of the value maps are the
* angles as provided by {@link #allowancesTrueWindAngles}.
*/
private final Map<Speed, Map<Bearing, Speed>> velocityPredictionPerTrueWindSpeedAndAngle;
/**
@@ -110,20 +95,34 @@ public class ORCCertificateImpl implements ORCCertificate {
private final Map<Speed, Speed> circularRandomSpeedPredictionPerTrueWindSpeed;
private final Map<Speed, Speed> nonSpinnakerSpeedPredictionPerTrueWindSpeed;
public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN,
String sailnumber, String boatName, String boatClassName,
Distance length, Duration gph,
Double cdl, TimePoint issueDate,
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle,
Map<Speed, Bearing> beatAngles, Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> beatAllowancePerTrueWindSpeed, Map<Speed, Bearing> runAngles,
Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
private final Speed[] allowancesTrueWindSpeeds;
private final Bearing[] allowancesTrueWindAngles;
/**
* Can be used to specify non-default TWS/TWA values for the time allowances / speed predictions.
*
* @param allowancesTrueWindSpeeds
* a monotonously-increasing sequence of true wind speed (TWS) values for the matrix of time allowances /
* velocity predictions for this certificate. See also
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_SPEEDS}.
* @param allowancesTrueWindAngles
* a monotonously-increasing sequence of true wind angle (TWA) values for the matrix of time allowances /
* velocity predictions for this certificate. See also
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}.
*/
public ORCCertificateImpl(Speed[] allowancesTrueWindSpeeds, Bearing[] allowancesTrueWindAngles,
String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName,
Distance length, Duration gph, Double cdl, TimePoint issueDate,
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle, Map<Speed, Bearing> beatAngles,
Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed, Map<Speed, Duration> beatAllowancePerTrueWindSpeed,
Map<Speed, Bearing> runAngles, Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> runAllowancePerTrueWindSpeed,
Map<Speed, Speed> windwardLeewardSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> longDistanceSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> circularRandomSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> nonSpinnakerSpeedPredictionsPerTrueWindSpeed) {
this.allowancesTrueWindAngles = allowancesTrueWindAngles;
this.allowancesTrueWindSpeeds = allowancesTrueWindSpeeds;
this.idConsistingOfNatAuthCertNoAndBIN = idConsistingOfNatAuthCertNoAndBIN;
this.sailNumber = sailnumber;
this.boatName = boatName;
@@ -145,6 +144,32 @@ public class ORCCertificateImpl implements ORCCertificate {
this.circularRandomSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(circularRandomSpeedPredictionsPerTrueWindSpeed);
this.nonSpinnakerSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(nonSpinnakerSpeedPredictionsPerTrueWindSpeed);
}
/**
* Uses the default TWS/TWA values from {@link ORCCertificate#ALLOWANCES_TRUE_WIND_SPEEDS} and
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}, respectively, for the matrix of time allowances
* or, conversely, the speed predictions.
*/
public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN,
String sailnumber, String boatName, String boatClassName,
Distance length, Duration gph,
Double cdl, TimePoint issueDate,
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle,
Map<Speed, Bearing> beatAngles, Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> beatAllowancePerTrueWindSpeed, Map<Speed, Bearing> runAngles,
Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
Map<Speed, Duration> runAllowancePerTrueWindSpeed,
Map<Speed, Speed> windwardLeewardSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> longDistanceSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> circularRandomSpeedPredictionsPerTrueWindSpeed,
Map<Speed, Speed> nonSpinnakerSpeedPredictionsPerTrueWindSpeed) {
this(ALLOWANCES_TRUE_WIND_SPEEDS, ALLOWANCES_TRUE_WIND_ANGLES, idConsistingOfNatAuthCertNoAndBIN, sailnumber,
boatName, boatClassName, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle,
beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles,
runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed,
windwardLeewardSpeedPredictionsPerTrueWindSpeed, longDistanceSpeedPredictionsPerTrueWindSpeed,
circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed);
}
@Override
public String getId() {
@@ -255,4 +280,14 @@ public class ORCCertificateImpl implements ORCCertificate {
return "Certificate with ID \""+getId()+"\" for "+getSailNumber()+" / "+getBoatName() + " - Issued on: " + (getIssueDate()==null?"n/a":getIssueDate().asDate())+" with GPH "+
Util.padPositiveValue(getGPHInSecondsToTheMile(), 1, 1, /* round */ true);
}
@Override
public Speed[] getTrueWindSpeeds() {
return allowancesTrueWindSpeeds;
}
@Override
public Bearing[] getTrueWindAngles() {
return allowancesTrueWindAngles;
}
}
@@ -86,4 +86,8 @@ public class SecuredDomainType extends HasPermissionsImpl {
"SWISS_TIMING_ARCHIVE_ACCOUNT");
public static final HasPermissions TRACTRAC_ACCOUNT = new SecuredDomainType("TRACTRAC_ACCOUNT");
public static final HasPermissions WIND_ESTIMATION_MODELS = new SecuredDomainType("WIND_ESTIMATION_MODELS");
public static final HasPermissions MARK_PROPERTIES = new SecuredDomainType("MARK_PROPERTIES");
public static final HasPermissions MARK_TEMPLATE = new SecuredDomainType("MARK_TEMPLATE");
public static final HasPermissions COURSE_TEMPLATE = new SecuredDomainType("COURSE_TEMPLATE");
public static final HasPermissions MARK_ROLE = new SecuredDomainType("MARK_ROLE");
}