Merge branch 'master' into bug4514

Change-Id: Id1ca0e271319ee209a1a3d7dc486ed1c7a6a25a6
This commit is contained in:
Axel Uhl committed 2018-04-19 20:50:47 +02:00
commit 452b5ff205
89 files changed
+1321 -505

No files matched your search

@@ -0,0 +1,15 @@
package com.sap.sailing.domain.common;
import java.io.Serializable;
public class ServiceException extends Exception implements Serializable {
private static final long serialVersionUID = 1690510580423647161L;
public ServiceException() {
}
public ServiceException(String message) {
super(message);
}
}
@@ -0,0 +1,14 @@
package com.sap.sailing.domain.common.dto;
public final class ExpeditionAllInOneConstants {
public static final String REQUEST_PARAMETER_IMPORT_MODE = "importMode";
public static final String REQUEST_PARAMETER_BOAT_CLASS = "boatClass";
public static final String REQUEST_PARAMETER_REGATTA_NAME = "regattaName";
public static final String REQUEST_PARAMETER_LOCALE = "locale";
public enum ImportMode{
NEW_COMPETITOR, NEW_EVENT, NEW_RACE;
}
private ExpeditionAllInOneConstants() {
}
}
@@ -483,7 +483,7 @@ public class IncrementalLeaderboardDTO extends LeaderboardDTO implements Increme
}
}
}
}
}
}
}
}
@@ -58,7 +58,8 @@ public class BravoFixImpl extends SensorFixImpl implements BravoFix {
@Override
public boolean isFoiling(Distance minimumRideHeight) {
return getRideHeight() == null ? false : getRideHeight().compareTo(minimumRideHeight) >= 0;
final Distance rideHeight = getRideHeight();
return rideHeight != null && rideHeight.compareTo(minimumRideHeight) >= 0;
}
@Override
@@ -263,6 +263,7 @@ import com.sap.sse.shared.media.VideoDescriptor;
import com.sap.sse.shared.media.impl.ImageDescriptorImpl;
import com.sap.sse.shared.media.impl.VideoDescriptorImpl;
import com.sap.sse.util.ThreadPoolUtil;
import com.sap.sse.util.impl.UUIDHelper;
public class DomainObjectFactoryImpl implements DomainObjectFactory {
private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName());
@@ -1626,7 +1627,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private RaceLogEvent loadRaceLogRevokeEvent(TimePoint createdAt, AbstractLogEventAuthor author,
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors,
DBObject dbObject) {
Serializable revokedEventId = Helpers
Serializable revokedEventId = UUIDHelper
.tryUuidConversion((Serializable) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_ID.name()));
String revokedEventType = (String) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_TYPE.name());
String revokedEventShortInfo = (String) dbObject.get(FieldNames.RACE_LOG_REVOKED_EVENT_SHORT_INFO.name());
@@ -1651,9 +1652,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
result = null; // the competitor wasn't found
dbObjectForUpdate = Optional.empty();
} else if (boatId == null) {
final Boat boat;
if (competitor.hasBoat() && (boat=((CompetitorWithBoat) competitor).getBoat()) != null) {
result = new RaceLogRegisterCompetitorEventImpl(createdAt, logicalTimePoint, author, id, passId, competitor, boat);
if (competitor.hasBoat()) {
result = new RaceLogRegisterCompetitorEventImpl(createdAt, logicalTimePoint, author, id, passId, competitor, ((CompetitorWithBoat) competitor).getBoat());
} else {
logger.warning("Bug2822: Competitor with ID "+competitorId+" already seems to have been migrated to one without boat."+
" But the RaceLogRegisterCompetitorEventImpl event loaded does not specify one either. We'll try to find a boat...");
@@ -2044,7 +2044,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private RegattaLogRevokeEvent loadRegattaLogRevokeEvent(TimePoint createdAt, AbstractLogEventAuthor author,
TimePoint logicalTimePoint, Serializable id, DBObject dbObject) {
Serializable revokedEventId = Helpers
Serializable revokedEventId = UUIDHelper
.tryUuidConversion((Serializable) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_ID.name()));
String revokedEventType = (String) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_TYPE.name());
String revokedEventShortInfo = (String) dbObject.get(FieldNames.REGATTA_LOG_REVOKED_EVENT_SHORT_INFO.name());
@@ -2092,7 +2092,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private RegattaLogCloseOpenEndedDeviceMappingEvent loadRegattaLogCloseOpenEndedDeviceMappingEvent(
TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id,
DBObject dbObject) {
Serializable deviceMappingEventId = Helpers
Serializable deviceMappingEventId = UUIDHelper
.tryUuidConversion((Serializable) dbObject.get(FieldNames.REGATTA_LOG_DEVICE_MAPPING_EVENT_ID.name()));
TimePoint closingTimePointInclusive = loadTimePoint(dbObject, FieldNames.REGATTA_LOG_CLOSING_TIMEPOINT);
return new RegattaLogCloseOpenEndedDeviceMappingEventImpl(createdAt, author, logicalTimePoint, id,
@@ -113,4 +113,6 @@ public interface SharedDomainFactory extends CompetitorFactory, BoatFactory {
RaceLogResolver getRaceLogResolver();
Mark getOrCreateMark(String name, MarkType markType);
Mark getOrCreateMark(Serializable id, String name, MarkType markType);
}
@@ -161,7 +161,8 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory {
return getOrCreateMark(name, name, markType);
}
private Mark getOrCreateMark(Serializable id, String name, MarkType markType) {
@Override
public Mark getOrCreateMark(Serializable id, String name, MarkType markType) {
return getOrCreateMark(id, name, markType, /* color */ null, /* shape */ null, /* pattern */ null);
}
@@ -44,7 +44,7 @@ public class SwissTimingConnectivityParamsHandler extends AbstractRaceTrackingCo
private static final String COMPETITOR_THREE_LETTER_IOC_CODE = "competitorThreeLetterIocCode";
private static final String COMPETITOR_NAME = "competitorName";
private static final String COMPETITOR_BOAT_ID = "competitorBoatId";
private static final String COMPETITOR_ID = "competitorId";
private static final String COMPETITOR_ID_AS_STRING = "competitorId"; // the competitor ID as String
private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm";
private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis";
private static final String START_LIST = "startList";
@@ -107,7 +107,7 @@ public class SwissTimingConnectivityParamsHandler extends AbstractRaceTrackingCo
private DBObject createCompetitorDBObject(Competitor competitor) {
final DBObject result = new BasicDBObject();
result.put(COMPETITOR_ID, competitor.getID());
result.put(COMPETITOR_ID_AS_STRING, competitor.getIdAsString());
result.put(COMPETITOR_BOAT_ID, competitor.getBoatID());
result.put(COMPETITOR_NAME, competitor.getName());
result.put(COMPETITOR_THREE_LETTER_IOC_CODE, competitor.getThreeLetterIOCCode());
@@ -150,15 +150,15 @@ public class SwissTimingConnectivityParamsHandler extends AbstractRaceTrackingCo
private Competitor createCompetitorFromDBObject(DBObject competitorDBObject) {
final Competitor result;
final String competitorId = (String) competitorDBObject.get(COMPETITOR_ID);
final String competitorIdAsString = (String) competitorDBObject.get(COMPETITOR_ID_AS_STRING);
final String boatID = (String) competitorDBObject.get(COMPETITOR_BOAT_ID);
final String threeLetterIOCCode = (String) competitorDBObject.get(COMPETITOR_THREE_LETTER_IOC_CODE);
final String name = (String) competitorDBObject.get(COMPETITOR_NAME);
final BasicDBList crewDBObject = (BasicDBList) competitorDBObject.get(COMPETITOR_CREW);
if (competitorId == null) {
if (competitorIdAsString == null) {
result = new CompetitorWithoutID(boatID, threeLetterIOCCode, name);
} else {
result = new CompetitorWithID(competitorId, boatID, threeLetterIOCCode, name, createCrewFromDBObject(crewDBObject));
result = new CompetitorWithID(competitorIdAsString, boatID, threeLetterIOCCode, name, createCrewFromDBObject(crewDBObject));
}
return result;
}
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.swisstimingadapter.Mark;
import com.sap.sse.common.Util;
public class CCGMessage {
private String raceId;
@@ -37,17 +38,11 @@ public class CCGMessage {
@Override
public String toString() {
// TODO Auto-generated method stub
String s = "";
for (Mark m : markList) {
s = s + "|" + m.getIndex() + ";" + m.getDescription() + ";";
for (String str : m.getDevices()) {
s = s + str + ";";
}
s = s.substring(0, s.length() - 1);
s += Util.join(";", m.getDeviceIds());
}
return "CCG|" +raceId + "|" + markList.size() + s;
}
}
@@ -6,6 +6,7 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -13,6 +14,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Speed;
@@ -205,9 +208,8 @@ public class MessageFileServiceImpl implements MessageFileService {
} else {
devicesNamesStartIndex = 2;
}
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]),
Arrays.asList(markDetails).subList(devicesNamesStartIndex, markDetails.length),
markType));
List<Serializable> markIds = Arrays.asList(markDetails).subList(devicesNamesStartIndex, markDetails.length).stream().map(idAsString->UUID.fromString(idAsString)).collect(Collectors.toList());
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]), markIds, markType));
}
return new CCGMessage(raceId, marks);
}
@@ -239,11 +239,11 @@ public class SailMasterConnectivityTest {
Iterator<Mark> mi = course1.getMarks().iterator();
Mark m11 = mi.next();
assertEquals("Lee Gate", m11.getDescription());
assertEquals(Arrays.asList(new String[] { "LG1", "LG2" }), m11.getDevices());
assertEquals(Arrays.asList(new String[] { "LG1", "LG2" }), m11.getDeviceIds());
assertEquals(1, m11.getIndex());
Mark m12 = mi.next();
assertEquals("Windward", m12.getDescription());
assertEquals(Arrays.asList(new String[] { "WW1" }), m12.getDevices());
assertEquals(Arrays.asList(new String[] { "WW1" }), m12.getDeviceIds());
assertEquals(2, m12.getIndex());
Race r2 = connector4712.getRace();
@@ -253,15 +253,15 @@ public class SailMasterConnectivityTest {
mi = course2.getMarks().iterator();
Mark m21 = mi.next();
assertEquals("Lee Gate", m21.getDescription());
assertEquals(Arrays.asList(new String[] { "LG1", "LG2" }), m21.getDevices());
assertEquals(Arrays.asList(new String[] { "LG1", "LG2" }), m21.getDeviceIds());
assertEquals(1, m21.getIndex());
Mark m22 = mi.next();
assertEquals("Windward", m22.getDescription());
assertEquals(Arrays.asList(new String[] { "WW1" }), m22.getDevices());
assertEquals(Arrays.asList(new String[] { "WW1" }), m22.getDeviceIds());
assertEquals(2, m22.getIndex());
Mark m23 = mi.next();
assertEquals("Offset", m23.getDescription());
assertEquals(Arrays.asList(new String[] { "OS1" }), m23.getDevices());
assertEquals(Arrays.asList(new String[] { "OS1" }), m23.getDeviceIds());
assertEquals(3, m23.getIndex());
}
@@ -10,6 +10,7 @@
*/
package com.sap.sailing.domain.swisstimingadapter.test.ui;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -248,11 +249,12 @@ public class EditCCG extends javax.swing.JDialog {
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
// TODO add your handling code here:
List<String> devices = new ArrayList<String>();
devices.add(jSailNumber1.getText());
if (jRadioButton2.isSelected())
devices.add(jSailNumber2.getText());
marks.add(new MarkWithDesc(jMarkDescription.getText(), Integer.parseInt(jMarkIndex.getText()),devices));
List<Serializable> deviceIds = new ArrayList<>();
deviceIds.add(jSailNumber1.getText());
if (jRadioButton2.isSelected()) {
deviceIds.add(jSailNumber2.getText());
}
marks.add(new MarkWithDesc(jMarkDescription.getText(), Integer.parseInt(jMarkIndex.getText()), deviceIds));
jMarkList.setListData(marks.toArray(new CCGMessage[0]));
}//GEN-LAST:event_jButton2ActionPerformed
@@ -272,13 +274,13 @@ public class EditCCG extends javax.swing.JDialog {
private class MarkWithDesc extends MarkImpl {
public MarkWithDesc(String description, int index, Iterable<String> devices) {
super(description, index, devices, /* markType */ null);
public MarkWithDesc(String description, int index, Iterable<Serializable> deviceIds) {
super(description, index, deviceIds, /* markType */ null);
}
@Override
public String toString(){
return "Mark " + getIndex() + ": " + getDescription() + getDevices();
return "Mark " + getIndex() + ": " + getDescription() + getDeviceIds();
}
}
@@ -11,5 +11,5 @@ public interface Competitor {
String getName();
String getID();
String getIdAsString();
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.swisstimingadapter;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@@ -46,7 +47,7 @@ public interface DomainFactory {
Pair<Competitor, Boat> createCompetitorWithID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, BoatClass boatClass);
Pair<Competitor, Boat> createCompetitorWithoutID(String boatID, String threeLetterIOCCode, String name, String raceId, BoatClass boatClass);
Pair<Competitor, Boat> createCompetitorWithoutID(String competitorId, String threeLetterIOCCode, String name, String raceId, BoatClass boatClass);
String getCompetitorID(String boatID, RaceType raceType);
@@ -54,7 +55,7 @@ public interface DomainFactory {
RaceDefinition createRaceDefinition(Regatta regatta, Race race, StartList startList, com.sap.sailing.domain.swisstimingadapter.Course course);
com.sap.sailing.domain.base.Mark getOrCreateMark(String trackerID);
com.sap.sailing.domain.base.Mark getOrCreateMark(Serializable trackerID, String description);
GPSFixMoving createGPSFix(TimePoint timePointOfTransmission, Fix fix);
@@ -71,7 +72,7 @@ public interface DomainFactory {
long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination);
ControlPoint getOrCreateControlPoint(Iterable<String> devices, MarkType markType);
ControlPoint getOrCreateControlPoint(String description, Iterable<Serializable> deviceIds, MarkType markType);
RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Map<Competitor, Boat> competitorsAndBoats,
List<ControlPoint> courseDefinition, String raceName, String raceIdForRaceDefinition);
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.swisstimingadapter;
import java.io.Serializable;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
@@ -13,7 +15,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
*
*/
public interface Fix {
String getBoatID();
Serializable getTrackedObjectId();
TrackerType getTrackerType();
@@ -1,11 +1,13 @@
package com.sap.sailing.domain.swisstimingadapter;
import java.io.Serializable;
public interface Mark extends WithDescription {
public enum MarkType { MANUAL, LINE_CROSSING, BUOY_ROUNDING };
int getIndex();
Iterable<String> getDevices();
Iterable<Serializable> getDeviceIds();
MarkType getMarkType();
}
@@ -1,7 +1,5 @@
package com.sap.sailing.domain.swisstimingadapter;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sse.common.Util;
@@ -10,6 +8,4 @@ public interface SwissTimingRaceTracker extends RaceTracker {
* The key for a SwissTiming race tracker is the triple of raceID, host name and port number.
*/
Util.Triple<String, String, Integer> getID();
Competitor getCompetitorByBoatIDAndRaceIDOrBoatClass(String boatID, String raceID, BoatClass boatClass);
}
@@ -29,7 +29,7 @@ public abstract class AbstractCompetitor implements Competitor {
return name;
}
public String getID() {
public String getIdAsString() {
return null;
}
@@ -5,17 +5,17 @@ import java.util.List;
import com.sap.sailing.domain.swisstimingadapter.CrewMember;
public class CompetitorWithID extends AbstractCompetitor {
private final String id;
private final String idAsString;
private List<CrewMember> crew;
public CompetitorWithID(String id, String boatID, String threeLetterIOCCode, String name, List<CrewMember> crew) {
public CompetitorWithID(String idAsString, String boatID, String threeLetterIOCCode, String name, List<CrewMember> crew) {
super(boatID, threeLetterIOCCode, name);
this.id = id;
this.idAsString = idAsString;
this.crew = crew;
}
public String getID() {
return id;
public String getIdAsString() {
return idAsString;
}
@Override
@@ -28,7 +28,7 @@ public class CompetitorWithID extends AbstractCompetitor {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((crew == null) ? 0 : crew.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((idAsString == null) ? 0 : idAsString.hashCode());
return result;
}
@@ -46,10 +46,10 @@ public class CompetitorWithID extends AbstractCompetitor {
return false;
} else if (!crew.equals(other.crew))
return false;
if (id == null) {
if (other.id != null)
if (idAsString == null) {
if (other.idAsString != null)
return false;
} else if (!id.equals(other.id))
} else if (!idAsString.equals(other.idAsString))
return false;
return true;
}
@@ -57,6 +57,6 @@ public class CompetitorWithID extends AbstractCompetitor {
@Override
public String toString() {
return "CompetitorWithID [boatID=" + getBoatID() + ", threeLetterIOCCode=" + getThreeLetterIOCCode() + ", name=" + getName()
+ ", id=" + id + ", crew=" + getCrew() + "]";
+ ", id=" + idAsString + ", crew=" + getCrew() + "]";
}
}
@@ -10,7 +10,7 @@ public class CompetitorWithoutID extends AbstractCompetitor {
}
@Override
public String getID() {
public String getIdAsString() {
return null;
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
@@ -58,6 +59,7 @@ import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.WithID;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.impl.UUIDHelper;
import difflib.PatchFailedException;
@@ -72,7 +74,7 @@ import difflib.PatchFailedException;
public class DomainFactoryImpl implements DomainFactory {
private final static Logger logger = Logger.getLogger(DomainFactoryImpl.class.getName());
private final Map<String, Regatta> raceIDToRegattaCache;
private final Map<Iterable<String>, ControlPoint> controlPointCache;
private final Map<Iterable<Serializable>, ControlPoint> controlPointCache;
private final Map<String, RaceType> raceTypeByID;
private final RaceType unknownRaceType;
private final com.sap.sailing.domain.base.DomainFactory baseDomainFactory;
@@ -80,7 +82,7 @@ public class DomainFactoryImpl implements DomainFactory {
public DomainFactoryImpl(com.sap.sailing.domain.base.DomainFactory baseDomainFactory) {
this.baseDomainFactory = baseDomainFactory;
raceIDToRegattaCache = new HashMap<String, Regatta>();
controlPointCache = new HashMap<Iterable<String>, ControlPoint>();
controlPointCache = new HashMap<>();
raceTypeByID = new HashMap<String, RaceType>();
for (OlympicRaceCode olympicRaceCode : OlympicRaceCode.values()) {
@@ -121,7 +123,8 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public Pair<Competitor, Boat> createCompetitorWithID(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, BoatClass boatClass) {
CompetitorAndBoatStore competitorAndBoatStore = baseDomainFactory.getCompetitorAndBoatStore();
CompetitorWithBoat domainCompetitor = competitorAndBoatStore.getExistingCompetitorWithBoatByIdAsString(competitor.getID());
final Serializable competitorId = UUIDHelper.tryUuidConversion(competitor.getIdAsString());
CompetitorWithBoat domainCompetitor = competitorAndBoatStore.getExistingCompetitorWithBoatById(competitorId);
if (domainCompetitor == null || competitorAndBoatStore.isCompetitorToUpdateDuringGetOrCreate(domainCompetitor)) {
List<DynamicPerson> teamMembers = new ArrayList<DynamicPerson>();
for (CrewMember crewMember: competitor.getCrew()) {
@@ -130,8 +133,8 @@ public class DomainFactoryImpl implements DomainFactory {
teamMembers.add(person);
}
DynamicTeam team = new TeamImpl(competitor.getName(), teamMembers, /* coach */ null);
DynamicBoat domainBoat = new BoatImpl(competitor.getID(), null, boatClass, competitor.getBoatID(), null);
domainCompetitor = competitorAndBoatStore.getOrCreateCompetitorWithBoat(competitor.getID(), competitor.getName(), null /* shortName */, null /*displayColor*/, null /*email*/, null, team,
DynamicBoat domainBoat = new BoatImpl(competitorId, null, boatClass, competitor.getBoatID(), null);
domainCompetitor = competitorAndBoatStore.getOrCreateCompetitorWithBoat(competitorId, competitor.getName(), null /* shortName */, null /*displayColor*/, null /*email*/, null, team,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null, domainBoat);
}
return new Pair<Competitor, Boat>(domainCompetitor, domainCompetitor.getBoat());
@@ -266,8 +269,8 @@ public class DomainFactoryImpl implements DomainFactory {
private Map<Competitor,Boat> createCompetitorsAndBoats(StartList startList, String raceId, BoatClass boatClass) {
Map<Competitor, Boat> result = new LinkedHashMap<>();
for (com.sap.sailing.domain.swisstimingadapter.Competitor swissTimingCompetitor : startList.getCompetitors()) {
Pair<Competitor,Boat> domainCompetitorAndBoat;
if (swissTimingCompetitor.getID() != null) {
Pair<Competitor, Boat> domainCompetitorAndBoat;
if (swissTimingCompetitor.getIdAsString() != null) {
domainCompetitorAndBoat = createCompetitorWithID(swissTimingCompetitor, boatClass);
} else {
domainCompetitorAndBoat = createCompetitorWithoutID(swissTimingCompetitor, raceId, boatClass);
@@ -280,7 +283,7 @@ public class DomainFactoryImpl implements DomainFactory {
private com.sap.sailing.domain.base.Course createCourse(String courseName, Course course) {
List<Waypoint> waypoints = new ArrayList<Waypoint>();
for (Mark mark : course.getMarks()) {
ControlPoint controlPoint = getOrCreateControlPoint(mark.getDevices(), getMarkType(mark.getMarkType()));
ControlPoint controlPoint = getOrCreateControlPoint(mark.getDescription(), mark.getDeviceIds(), getMarkType(mark.getMarkType()));
Waypoint waypoint = baseDomainFactory.createWaypoint(controlPoint, /* passingInstruction */ PassingInstruction.None);
waypoints.add(waypoint);
}
@@ -302,44 +305,44 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public ControlPoint getOrCreateControlPoint(Iterable<String> devices, MarkType markType) {
public ControlPoint getOrCreateControlPoint(String description, Iterable<Serializable> deviceIds, MarkType markType) {
ControlPoint result;
synchronized (controlPointCache) {
result = controlPointCache.get(devices);
result = controlPointCache.get(deviceIds);
if (result == null) {
switch (Util.size(devices)) {
switch (Util.size(deviceIds)) {
case 1:
result = getOrCreateMark(devices.iterator().next(), markType);
result = getOrCreateMark(description, deviceIds.iterator().next(), markType);
break;
case 2:
Iterator<String> markNameIter = devices.iterator();
String left = markNameIter.next();
String right = markNameIter.next();
result = baseDomainFactory.createControlPointWithTwoMarks(getOrCreateMark(left), getOrCreateMark(right), left + "/" + right);
Iterator<Serializable> markNameIter = deviceIds.iterator();
final Serializable idLeft = markNameIter.next();
final Serializable idRight = markNameIter.next();
result = baseDomainFactory.createControlPointWithTwoMarks(getOrCreateMark(idLeft, description), getOrCreateMark(idRight, description), description);
break;
default:
throw new RuntimeException(
"Don't know how to handle control points with number of devices neither 1 nor 2. Was "
+ Util.size(devices));
+ Util.size(deviceIds));
}
controlPointCache.put(devices, result);
controlPointCache.put(deviceIds, result);
}
}
return result;
}
private ControlPoint getOrCreateMark(String name, MarkType markType) {
return baseDomainFactory.getOrCreateMark(name, markType);
private ControlPoint getOrCreateMark(String description, Serializable id, MarkType markType) {
return baseDomainFactory.getOrCreateMark(id, description, markType);
}
/**
* @param trackerID
* @param trackerId
* the "device name" and the "sail number" in case of an {@link MessageType#RPD RPD} message, used as the mark's
* {@link Named#getName() name} and {@link WithID#getId() ID}.
*/
@Override
public com.sap.sailing.domain.base.Mark getOrCreateMark(String trackerID) {
return baseDomainFactory.getOrCreateMark(trackerID);
public com.sap.sailing.domain.base.Mark getOrCreateMark(Serializable trackerId, String description) {
return baseDomainFactory.getOrCreateMark(trackerId, description);
}
@Override
@@ -354,7 +357,7 @@ public class DomainFactoryImpl implements DomainFactory {
List<com.sap.sse.common.Util.Pair<com.sap.sailing.domain.base.ControlPoint, PassingInstruction>> newDomainControlPoints = new ArrayList<com.sap.sse.common.Util.Pair<com.sap.sailing.domain.base.ControlPoint, PassingInstruction>>();
for (Mark mark : marks) {
// TODO bug 1043: propagate the mark names to the waypoint names
com.sap.sailing.domain.base.ControlPoint domainControlPoint = getOrCreateControlPoint(mark.getDevices(), getMarkType(mark.getMarkType()));
com.sap.sailing.domain.base.ControlPoint domainControlPoint = getOrCreateControlPoint(mark.getDescription(), mark.getDeviceIds(), getMarkType(mark.getMarkType()));
newDomainControlPoints.add(new com.sap.sse.common.Util.Pair<>(domainControlPoint, PassingInstruction.None));
}
courseToUpdate.update(newDomainControlPoints, baseDomainFactory);
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.io.Serializable;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
@@ -8,7 +10,7 @@ import com.sap.sailing.domain.swisstimingadapter.Fix;
import com.sap.sailing.domain.swisstimingadapter.TrackerType;
public class FixImpl implements Fix {
private final String boatID;
private final Serializable trackedObjectId;
private final TrackerType trackerType;
private final Long ageOfDataInMilliseconds;
private final Position position;
@@ -25,11 +27,11 @@ public class FixImpl implements Fix {
*/
private final String boatIRM;
public FixImpl(String boatID, TrackerType trackerType, Long ageOfDataInMilliseconds, Position position,
public FixImpl(Serializable trackedObjectId, TrackerType trackerType, Long ageOfDataInMilliseconds, Position position,
SpeedWithBearing speed, Integer nextMarkIndex, Integer rank, Speed averageSpeedOverGroundPerLeg,
Speed velocityMadeGood, Distance distanceToLeader, Distance distanceToNextMark, String boatIRM) {
super();
this.boatID = boatID;
this.trackedObjectId = trackedObjectId;
this.trackerType = trackerType;
this.ageOfDataInMilliseconds = ageOfDataInMilliseconds;
this.position = position;
@@ -49,8 +51,8 @@ public class FixImpl implements Fix {
}
@Override
public String getBoatID() {
return boatID;
public Serializable getTrackedObjectId() {
return trackedObjectId;
}
@Override
@@ -1,25 +1,28 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.swisstimingadapter.Mark;
import com.sap.sse.common.Util;
public class MarkImpl implements Mark {
private final String description;
private final int index;
private final Iterable<String> devices;
private final Iterable<Serializable> deviceIds;
private final MarkType markType;
public MarkImpl(String description, int index, Iterable<String> devices, MarkType markType) {
public MarkImpl(String description, int index, Iterable<Serializable> devices, MarkType markType) {
super();
this.description = description;
this.index = index;
this.markType = markType;
ArrayList<String> l = new ArrayList<String>();
for (String device : devices) {
List<Serializable> l = new ArrayList<>();
for (Serializable device : devices) {
l.add(device);
}
this.devices = l;
this.deviceIds = l;
}
@Override
@@ -38,8 +41,8 @@ public class MarkImpl implements Mark {
}
@Override
public Iterable<String> getDevices() {
return devices;
public Iterable<Serializable> getDeviceIds() {
return deviceIds;
}
@Override
@@ -49,11 +52,7 @@ public class MarkImpl implements Mark {
result.append(": ");
result.append(getDescription());
result.append(" [");
for (String device : getDevices()) {
result.append(device);
result.append(", ");
}
result.delete(result.length()-2, result.length());
result.append(Util.join(", ", getDeviceIds()));
result.append("]");
return result.toString();
}
@@ -25,6 +25,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.common.AbstractBearing;
@@ -55,6 +56,7 @@ import com.sap.sailing.domain.swisstimingadapter.TrackerType;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.impl.UUIDHelper;
/**
* Implements the connector to the SwissTiming Sail Master system. It uses a host name and port number to establish the
@@ -283,7 +285,7 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
private void notifyListenersTMD(SailMasterMessage message) {
// example message: TMD|W4702|NZL 75|1|4;;00:49:43
String raceID = message.getSections()[1];
String boatID = message.getSections()[2];
String competitorIdAsString = message.getSections()[2];
int count = Integer.valueOf(message.getSections()[3]);
List<Util.Triple<Integer, Integer, Long>> markIndicesRanksAndTimesSinceStartInMilliseconds = new ArrayList<Util.Triple<Integer,Integer,Long>>();
for (int i = 0; i < count; i++) {
@@ -296,7 +298,7 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
}
for (SailMasterListener listener : getListeners()) {
try {
listener.receivedTimingData(raceID, boatID, markIndicesRanksAndTimesSinceStartInMilliseconds);
listener.receivedTimingData(raceID, competitorIdAsString, markIndicesRanksAndTimesSinceStartInMilliseconds);
} catch (Exception e) {
logger.info("Exception occurred trying to notify listener "+listener+" about "+message+": "+e.getMessage());
logger.throwing(SailMasterConnectorImpl.class.getName(), "notifyListenersTMD", e);
@@ -359,12 +361,12 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
String raceID = sections[1];
final RaceStatus raceStatus;
final RacingStatus racingStatus;
if (sections[2].contains(",")) {
final String[] raceStatusIntAndRacingStatusInt = sections[2].split(",");
final String[] raceStatusIntAndRacingStatusInt = sections[2].split(",");
if (raceStatusIntAndRacingStatusInt.length > 1) {
raceStatus = RaceStatus.values()[Integer.valueOf(raceStatusIntAndRacingStatusInt[0])];
racingStatus = RacingStatus.values()[Integer.valueOf(raceStatusIntAndRacingStatusInt[1])];
} else {
raceStatus = RaceStatus.values()[Integer.valueOf(sections[2])];
raceStatus = RaceStatus.values()[Integer.valueOf(raceStatusIntAndRacingStatusInt[0])];
racingStatus = null;
}
TimePoint timePoint = new MillisecondsTimePoint(parseTimeAndDateISO(sections[3], raceID));
@@ -386,45 +388,47 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
final String[] fixSections = sections[9+i].split(";");
final boolean postVersion1_0 = sections[9+i].split(";", -1).length >= 14;
if (fixSections.length > 2) {
final String boatID = fixSections[fixDetailIndex++];
final TrackerType trackerType = TrackerType.values()[Integer.valueOf(fixSections[fixDetailIndex++])];
final Long ageOfDataInMilliseconds = 1000l * Long.valueOf(fixSections[fixDetailIndex++]);
final Position position = new DegreePosition(Double.valueOf(fixSections[fixDetailIndex++]),
Double.valueOf(fixSections[fixDetailIndex++]));
final Double speedOverGroundInKnots = Double.valueOf(fixSections[fixDetailIndex++]);
final int alsIndex = postVersion1_0 ? fixDetailIndex+1 : fixDetailIndex;
final int vmgIndex = postVersion1_0 ? fixDetailIndex : fixDetailIndex+1;
final Speed averageSpeedOverGround = fixSections[alsIndex].trim().length() == 0 ? null
: new KnotSpeedImpl(Double.valueOf(fixSections[alsIndex]));
final Speed velocityMadeGood = fixSections[vmgIndex].trim().length() == 0 ? null : new KnotSpeedImpl(
Double.valueOf(fixSections[vmgIndex]));
fixDetailIndex += 2;
final AbstractBearing cog = new DegreeBearingImpl(
Double.valueOf(fixSections[fixDetailIndex++]));
final SpeedWithBearing speed = new KnotSpeedWithBearingImpl(speedOverGroundInKnots, cog);
final Integer nextMarkIndex = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : Integer
.valueOf(fixSections[fixDetailIndex]);
fixDetailIndex++;
final Integer rank = fixSections.length <= fixDetailIndex || fixSections[fixDetailIndex].trim().length() == 0 ? null
: Integer.valueOf(fixSections[fixDetailIndex]);
fixDetailIndex++;
final Distance distanceToLeader = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : new MeterDistance(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
final Distance distanceToNextMark = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : new MeterDistance(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
final String boatIRM; // the "disqualification" or "MaxPointReason"
if (postVersion1_0 && fixSections.length > fixDetailIndex) {
boatIRM = fixSections[fixDetailIndex++];
} else {
boatIRM = null;
final String trackedObjectIdAsString = fixSections[fixDetailIndex++].trim();
if (trackedObjectIdAsString != null && !trackedObjectIdAsString.isEmpty()) {
final TrackerType trackerType = TrackerType.values()[Integer.valueOf(fixSections[fixDetailIndex++])];
final Long ageOfDataInMilliseconds = 1000l * Long.valueOf(fixSections[fixDetailIndex++]);
final Position position = new DegreePosition(Double.valueOf(fixSections[fixDetailIndex++]),
Double.valueOf(fixSections[fixDetailIndex++]));
final Double speedOverGroundInKnots = Double.valueOf(fixSections[fixDetailIndex++]);
final int alsIndex = postVersion1_0 ? fixDetailIndex+1 : fixDetailIndex;
final int vmgIndex = postVersion1_0 ? fixDetailIndex : fixDetailIndex+1;
final Speed averageSpeedOverGround = fixSections[alsIndex].trim().length() == 0 ? null
: new KnotSpeedImpl(Double.valueOf(fixSections[alsIndex]));
final Speed velocityMadeGood = fixSections[vmgIndex].trim().length() == 0 ? null : new KnotSpeedImpl(
Double.valueOf(fixSections[vmgIndex]));
fixDetailIndex += 2;
final AbstractBearing cog = new DegreeBearingImpl(
Double.valueOf(fixSections[fixDetailIndex++]));
final SpeedWithBearing speed = new KnotSpeedWithBearingImpl(speedOverGroundInKnots, cog);
final Integer nextMarkIndex = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : Integer
.valueOf(fixSections[fixDetailIndex]);
fixDetailIndex++;
final Integer rank = fixSections.length <= fixDetailIndex || fixSections[fixDetailIndex].trim().length() == 0 ? null
: Integer.valueOf(fixSections[fixDetailIndex]);
fixDetailIndex++;
final Distance distanceToLeader = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : new MeterDistance(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
final Distance distanceToNextMark = fixSections.length <= fixDetailIndex
|| fixSections[fixDetailIndex].trim().length() == 0 ? null : new MeterDistance(
Double.valueOf(fixSections[fixDetailIndex]));
fixDetailIndex++;
final String boatIRM; // the "disqualification" or "MaxPointReason"
if (postVersion1_0 && fixSections.length > fixDetailIndex) {
boatIRM = fixSections[fixDetailIndex++];
} else {
boatIRM = null;
}
fixes.add(new FixImpl(UUIDHelper.tryUuidConversion(trackedObjectIdAsString), trackerType, ageOfDataInMilliseconds, position, speed, nextMarkIndex,
rank, averageSpeedOverGround, velocityMadeGood, distanceToLeader, distanceToNextMark, boatIRM));
}
fixes.add(new FixImpl(boatID, trackerType, ageOfDataInMilliseconds, position, speed, nextMarkIndex,
rank, averageSpeedOverGround, velocityMadeGood, distanceToLeader, distanceToNextMark, boatIRM));
}
}
Set<SailMasterListener> allListeners = getListeners();
@@ -625,7 +629,7 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
devicesNamesStartIndex = 2;
}
marks.add(new MarkImpl(markDetails[1], Integer.valueOf(markDetails[0]),
Arrays.asList(markDetails).subList(devicesNamesStartIndex, markDetails.length),
Arrays.asList(markDetails).subList(devicesNamesStartIndex, markDetails.length).stream().map(idAsString->UUIDHelper.tryUuidConversion(idAsString)).collect(Collectors.toList()),
markType));
}
return new CourseImpl(courseConfigurationMessage.getSections()[1], marks);
@@ -99,7 +99,6 @@ public class SwissTimingAdapterImpl implements SwissTimingAdapter {
teams.put(t.getTeamID(), t);
}
}
for (Object o : regattaResults.getPersonOrBoatOrTeam()) {
if (o instanceof Event) {
Event event = (Event) o;
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.text.ParseException;
@@ -12,6 +13,7 @@ import java.util.Map;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -42,7 +44,6 @@ import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.Fix;
import com.sap.sailing.domain.swisstimingadapter.Race;
import com.sap.sailing.domain.swisstimingadapter.RaceStatus;
import com.sap.sailing.domain.swisstimingadapter.RaceType;
import com.sap.sailing.domain.swisstimingadapter.RacingStatus;
import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector;
import com.sap.sailing.domain.swisstimingadapter.SailMasterListener;
@@ -276,13 +277,13 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
case JURY:
case TIMINGSCORING:
case UNIDENTIFIED:
String trackerID = fix.getBoatID();
Mark mark = domainFactory.getOrCreateMark(trackerID);
Serializable trackerID = fix.getTrackedObjectId();
Mark mark = domainFactory.getOrCreateMark(trackerID, /* description */ null);
trackedRace.recordFix(mark, gpsFix);
break;
case COMPETITOR:
String boatID = fix.getBoatID();
Competitor competitor = getCompetitorByBoatIDAndRaceIDOrBoatClass(boatID, raceID, boatClass);
Serializable competitorId = fix.getTrackedObjectId();
Competitor competitor = getDomainFactory().getBaseDomainFactory().getExistingCompetitorById(competitorId);
if (competitor == null) {
// TODO: read startlist again from Manage2Sail
// use competitorStore.isCompetitorToUpdateDuringGetOrCreate(result)
@@ -291,7 +292,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
DynamicGPSFixTrack<Competitor, GPSFixMoving> competitorTrack = trackedRace.getTrack(competitor);
competitorTrack.addGPSFix(gpsFix);
} else {
logger.info("Unknown competitor " + boatID + " found for race with id " + raceID);
logger.info("Unknown competitor " + competitorId + " found for race with id " + raceID);
}
break;
default:
@@ -310,41 +311,15 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
}
private Competitor getCompetitorByBoatIDAndRaceType(String boatID, RaceType raceType) {
return domainFactory.getBaseDomainFactory().getExistingCompetitorById(domainFactory.getCompetitorID(boatID, raceType));
}
private Competitor getCompetitorByBoatIDAndBoatClass(String boatID, BoatClass boatClass) {
return domainFactory.getBaseDomainFactory().getExistingCompetitorById(domainFactory.getCompetitorID(boatID, boatClass));
}
@Override
public Competitor getCompetitorByBoatIDAndRaceIDOrBoatClass(String boatID, String raceID, BoatClass boatClass) {
Competitor result = null;
// first look into the temp cache
result = competitorsByBoatId.get(boatID);
if (result == null) {
if (boatClass != null) {
result = getCompetitorByBoatIDAndBoatClass(boatID, boatClass);
} else {
RaceType raceType = domainFactory.getRaceTypeFromRaceID(raceID);
if (raceType != null) {
result = getCompetitorByBoatIDAndRaceType(boatID, raceType);
}
}
}
return result;
}
@Override
public void receivedTimingData(String raceID, String boatID,
public void receivedTimingData(String raceID, String competitorIdAsString,
List<com.sap.sse.common.Util.Triple<Integer, Integer, Long>> markIndicesRanksAndTimesSinceStartInMilliseconds) {
assert this.raceID.equals(raceID);
if (!useInternalMarkPassingAlgorithm) {
if (isTrackedRaceStillReachable()) {
Competitor competitor = getCompetitorByBoatIDAndRaceIDOrBoatClass(boatID, raceID, boatClass);
final Competitor competitor = domainFactory.getBaseDomainFactory().getExistingCompetitorById(UUID.fromString(competitorIdAsString));
if (competitor == null) {
logger.info("Received timing data for boat ID " + boatID + " in race " + raceID
logger.info("Received timing data for boat ID " + competitorIdAsString + " in race " + raceID
+ " but couldn't find a competitor with that boat ID in this race. Ignoring.");
} else {
// the list of mark indices and time stamps is partial and usually only shows the last mark passing;
@@ -378,8 +353,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
final TimePoint startTime = trackedRace.getStartOfRace();
MillisecondsTimePoint timePoint = new MillisecondsTimePoint(startTime.asMillis()
+ markIndexRankAndTimeSinceStartInMilliseconds.getC());
MarkPassing markPassing = domainFactory.createMarkPassing(timePoint, waypoint,
getCompetitorByBoatIDAndRaceIDOrBoatClass(boatID, raceID, boatClass));
MarkPassing markPassing = domainFactory.createMarkPassing(timePoint, waypoint, competitor);
markPassingsByMarkIndex.put(markIndexRankAndTimeSinceStartInMilliseconds.getA(),
markPassing);
} else {
@@ -387,7 +361,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
+ trackedRace.getRace().getName()
+ " before having received a race start time."
+ " Queueing message for re-application when a start time has been received.");
tmdMessageQueue.enqueue(raceID, boatID,
tmdMessageQueue.enqueue(raceID, competitorIdAsString,
markIndicesRanksAndTimesSinceStartInMilliseconds);
}
}
@@ -474,7 +448,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
// temp
CompetitorAndBoatStore competitorStore = domainFactory.getBaseDomainFactory().getCompetitorAndBoatStore();
for (com.sap.sailing.domain.swisstimingadapter.Competitor c : startList.getCompetitors()) {
Competitor existingCompetitor = competitorStore.getExistingCompetitorByIdAsString(c.getID());
Competitor existingCompetitor = competitorStore.getExistingCompetitorByIdAsString(c.getIdAsString());
if (existingCompetitor != null) {
competitorsByBoatId.put(c.getBoatID(), existingCompetitor);
}
@@ -8,6 +8,7 @@ import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor;
@@ -30,14 +31,14 @@ public class TMDMessageQueue {
private static class TMDMessageContents {
private final String raceID;
private final String boatID;
private final String competitorIdAsString;
private final List<Util.Triple<Integer, Integer, Long>> markIndicesRanksAndTimesSinceStartInMilliseconds;
public TMDMessageContents(String raceID, String boatID,
public TMDMessageContents(String raceID, String competitorIdAsString,
List<Util.Triple<Integer, Integer, Long>> markIndicesRanksAndTimesSinceStartInMilliseconds) {
super();
this.raceID = raceID;
this.boatID = boatID;
this.competitorIdAsString = competitorIdAsString;
this.markIndicesRanksAndTimesSinceStartInMilliseconds = markIndicesRanksAndTimesSinceStartInMilliseconds;
}
@@ -45,8 +46,8 @@ public class TMDMessageQueue {
return raceID;
}
public String getBoatID() {
return boatID;
public String getCompetitorIdAsString() {
return competitorIdAsString;
}
public List<Util.Triple<Integer, Integer, Long>> getMarkIndicesRanksAndTimesSinceStartInMilliseconds() {
@@ -55,7 +56,7 @@ public class TMDMessageQueue {
@Override
public String toString() {
StringBuilder result = new StringBuilder("TMD|"+getRaceID()+"|"+getBoatID()+"|");
StringBuilder result = new StringBuilder("TMD|"+getRaceID()+"|"+getCompetitorIdAsString()+"|");
result.append(getMarkIndicesRanksAndTimesSinceStartInMilliseconds().size());
result.append('|');
for (Util.Triple<Integer, Integer, Long> markIndexRankAndTimeSinceStartInMilliseconds : getMarkIndicesRanksAndTimesSinceStartInMilliseconds()) {
@@ -82,8 +83,8 @@ public class TMDMessageQueue {
queuedMessages = new HashSet<>();
}
public synchronized void enqueue(String raceID, String boatID, List<Util.Triple<Integer, Integer, Long>> markIndicesRanksAndTimesSinceStartInMilliseconds) {
final TMDMessageContents message = new TMDMessageContents(raceID, boatID, markIndicesRanksAndTimesSinceStartInMilliseconds);
public synchronized void enqueue(String raceID, String competitorIdAsString, List<Util.Triple<Integer, Integer, Long>> markIndicesRanksAndTimesSinceStartInMilliseconds) {
final TMDMessageContents message = new TMDMessageContents(raceID, competitorIdAsString, markIndicesRanksAndTimesSinceStartInMilliseconds);
queuedMessages.add(message);
logger.info("Queued TMD message "+message+" for replay when start time has been received");
}
@@ -101,10 +102,9 @@ public class TMDMessageQueue {
// otherwise, this could lead to the "guessed time" to
while (i.hasNext()) {
TMDMessageContents messageContents = i.next();
Competitor competitor = raceTracker.getCompetitorByBoatIDAndRaceIDOrBoatClass(messageContents.getBoatID(),
messageContents.getRaceID(), raceTracker.getBoatClass());
Competitor competitor = raceTracker.getDomainFactory().getBaseDomainFactory().getExistingCompetitorById(UUID.fromString(messageContents.getCompetitorIdAsString()));
if (competitor == null) {
logger.info("Received timing data for boat ID " + messageContents.getBoatID() + " in race " + messageContents.getRaceID()
logger.info("Received timing data for boat ID " + messageContents.getCompetitorIdAsString() + " in race " + messageContents.getRaceID()
+ " but couldn't find a competitor with that boat ID in this race. Ignoring.");
} else {
NavigableSet<MarkPassing> oldMarkPassings = trackedRace.getMarkPassings(competitor);
@@ -156,7 +156,7 @@ public class TMDMessageQueue {
while (i.hasNext()) {
TMDMessageContents messageContents = i.next();
logger.info("Re-Playing TMD message "+messageContents+" with new race start time "+trackedRace.getStartOfRace());
raceTracker.receivedTimingData(messageContents.getRaceID(), messageContents.getBoatID(),
raceTracker.receivedTimingData(messageContents.getRaceID(), messageContents.getCompetitorIdAsString(),
messageContents.getMarkIndicesRanksAndTimesSinceStartInMilliseconds());
i.remove();
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -308,7 +309,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i
competitorByHashValue.put(hashValue, competitorAndBoat.getA());
} else {
// consider it a mark
Mark mark = domainFactory.getOrCreateMark(sailNumberOrTrackerID.trim());
Mark mark = domainFactory.getOrCreateMark(sailNumberOrTrackerID.trim(), name);
Map<String, Mark> marksOfCurrentRace = marksPerRaceIDPerMarkID.get(currentRaceID);
if (marksOfCurrentRace == null) {
marksOfCurrentRace = new HashMap<>();
@@ -323,12 +324,12 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i
@Override
public void mark(MarkType markType, String name, byte index, String id1, String id2, short windSpeedInKnots,
short trueWindDirectionInDegrees) {
final List<String> markNames = new ArrayList<>();
markNames.add(id1.trim());
final List<Serializable> markNamesAsIds = new ArrayList<>();
markNamesAsIds.add(id1.trim());
if (id2 != null && !id2.trim().isEmpty()) {
markNames.add(id2.trim());
markNamesAsIds.add(id2.trim());
}
final ControlPoint controlPoint = domainFactory.getOrCreateControlPoint(markNames, getMarkType(markType));
final ControlPoint controlPoint = domainFactory.getOrCreateControlPoint(name, markNamesAsIds, getMarkType(markType));
if (index == 0) {
currentCourseDefinition = new ArrayList<>();
}
@@ -0,0 +1,40 @@
package com.sap.sailing.gwt.common.client.suggestion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.common.Util;
import com.sap.sse.common.util.NaturalComparator;
public class RegattaSuggestOracle extends AbstractListSuggestOracle<RegattaDTO> implements RegattasDisplayer {
public RegattaSuggestOracle() {
}
@Override
protected String createSuggestionKeyString(RegattaDTO value) {
return value.getName();
}
@Override
protected String createSuggestionAdditionalDisplayString(RegattaDTO value) {
return value.boatClass.getName();
}
@Override
protected Iterable<String> getMatchingStrings(RegattaDTO value) {
return Arrays.asList(value.getName(), value.boatClass.getName());
}
@Override
public void fillRegattas(Iterable<RegattaDTO> result) {
final List<RegattaDTO> sorted = new ArrayList<>();
final NaturalComparator nameComparator = new NaturalComparator(/* case sensitive */ false);
Util.addAll(result, sorted);
Collections.sort(sorted, (r1, r2)->nameComparator.compare(r1.getName(), r2.getName()));
setSelectableValues(sorted);
}
}
@@ -146,6 +146,7 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl
true, /* enablePager */true, /* filterCompetitorWithBoat */ canBoatsOfCompetitorsChangePerRace, /* filterCompetitorsWithoutBoat */ !canBoatsOfCompetitorsChangePerRace);
registeredCompetitorsTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */
true, /* enablePager */false, /* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ false);
registeredCompetitorsTable.getSelectionModel().addSelectionChangeHandler(event -> validateAndUpdate());
allCompetitorsPanel.add(allCompetitorsTable);
registeredCompetitorsPanel.add(registeredCompetitorsTable);
VerticalPanel movePanel = new VerticalPanel();
@@ -229,6 +230,7 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl
protected void addImportedCompetitorsToRegisteredCompetitorsTableAndRemoveFromAllCompetitorsTable(Iterable<CompetitorWithBoatDTO> competitorsImported) {
allCompetitorsTable.getFilterField().removeAll(competitorsImported);
registeredCompetitorsTable.getFilterField().addAll(competitorsImported);
validateAndUpdate();
}
private void moveSelected(CompetitorTableWrapper<RefreshableMultiSelectionModel<CompetitorWithBoatDTO>> from,
@@ -319,4 +321,8 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl
public void moveFromPoolToRegistered(Collection<CompetitorWithBoatDTO> registeredCompetitors) {
move(allCompetitorsTable, registeredCompetitorsTable, registeredCompetitors);
}
public Set<CompetitorWithBoatDTO> getSelectedRegisteredCompetitors() {
return registeredCompetitorsTable.getSelectionModel().getSelectedSet();
}
}
@@ -20,39 +20,49 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.Util.Triple;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
/**
* This is the UI wizard to be executed after
* {@link com.sap.sailing.server.gateway.trackfiles.impl.ExpeditionAllInOneImporter ExpeditionAllInOneImporter}
* succeeded running. This provides the functionality to add new {@link com.sap.sailing.domain.base.Competitor
* Competitors} to the {@link com.sap.sailing.domain.base.Regatta Regatta} and automatically maps the imported GPS and
* Bravo tracks to a selected {@link com.sap.sailing.domain.base.Competitor Competitor}. In addition, the
* {@link com.sap.sailing.domain.tracking.TrackedRace TrackedRace} is retracked after the process to ensure that newly
* added competitors are correctly visible afterwards. After the import finished successfully, a dialog is provided to
* the user that shows links to the event page and race board.
*/
public class ExpeditionAllInOneAfterImportHandler {
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
private final RegattaNameAndRaceName regattaAndRaceIdentifier;
private final String leaderboardGroupName;
private final String raceColumnName;
private final String fleetName;
protected EventDTO event;
private RegattaDTO regatta;
private StrippedLeaderboardDTO leaderboard;
private List<TrackFileImportDeviceIdentifierDTO> gpsFixesDeviceIDs;
private List<TrackFileImportDeviceIdentifierDTO> sensorFixesDeviceIDs;
private final String sensorImporterType;
private List<Triple<String, String, String>> raceEntries;
private String regattaName;
public ExpeditionAllInOneAfterImportHandler(UUID eventId, String regattaName, String leaderboardName,
String leaderboardGroupName, String raceName, String raceColumnName, String fleetName,
String leaderboardGroupName, List<Triple<String, String, String>> raceEntries,
List<String> gpsDeviceIds, List<String> sensorDeviceIds, String sensorImporterType,
final SailingServiceAsync sailingService, final ErrorReporter errorReporter,
final StringMessages stringMessages) {
this.leaderboardGroupName = leaderboardGroupName;
this.raceColumnName = raceColumnName;
this.fleetName = fleetName;
this.sensorImporterType = sensorImporterType;
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
this.regattaAndRaceIdentifier = new RegattaNameAndRaceName(regattaName, raceName);
this.raceEntries = raceEntries;
this.regattaName = regattaName;
sailingService.getEventById(eventId, false, new DataLoadingCallback<EventDTO>() {
@Override
@@ -89,32 +99,77 @@ public class ExpeditionAllInOneAfterImportHandler {
}
});
}
private class RegattaLogCompetitorRegistrationAndSelectionDialog extends RegattaLogCompetitorRegistrationDialog {
public RegattaLogCompetitorRegistrationAndSelectionDialog(String boatClass, SailingServiceAsync sailingService,
StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName,
boolean canBoatsOfCompetitorsChangePerRace) {
this(boatClass, sailingService, stringMessages, errorReporter, editable, leaderboardName,
canBoatsOfCompetitorsChangePerRace, new ValidatorForCompetitorRegistrationDialog(stringMessages),
new CallbackForCompetitorRegistrationDialog());
}
public RegattaLogCompetitorRegistrationAndSelectionDialog(String boatClass, SailingServiceAsync sailingService,
StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName,
boolean canBoatsOfCompetitorsChangePerRace, ValidatorForCompetitorRegistrationDialog validator,
CallbackForCompetitorRegistrationDialog callback) {
super(boatClass, sailingService, stringMessages, errorReporter, editable, leaderboardName,
canBoatsOfCompetitorsChangePerRace, validator, callback);
validator.setCompetitorRegistrationsPanel(competitorRegistrationsPanel);
callback.setCompetitorRegistrationsPanel(competitorRegistrationsPanel);
}
}
private static class ValidatorForCompetitorRegistrationDialog implements Validator<Set<CompetitorWithBoatDTO>> {
private CompetitorRegistrationsPanel competitorRegistrationsPanel;
private StringMessages stringMessages;
public ValidatorForCompetitorRegistrationDialog(StringMessages stringMessages) {
this.stringMessages = stringMessages;
}
@Override
public String getErrorMessage(Set<CompetitorWithBoatDTO> valueToValidate) {
if (competitorRegistrationsPanel == null || competitorRegistrationsPanel.getSelectedRegisteredCompetitors().size() != 1) {
return stringMessages.selectOneCompetitorToMapTheImportedData();
}
return null;
}
public void setCompetitorRegistrationsPanel(CompetitorRegistrationsPanel competitorRegistrationsPanel) {
this.competitorRegistrationsPanel = competitorRegistrationsPanel;
}
}
private class CallbackForCompetitorRegistrationDialog extends CancelImportDialogCallback<Set<CompetitorWithBoatDTO>> {
private CompetitorRegistrationsPanel competitorRegistrationsPanel;
@Override
public void ok(Set<CompetitorWithBoatDTO> competitors) {
if (competitors.isEmpty()) {
Window.alert(stringMessages.importCanceledNoCompetitorAdded());
} else {
sailingService.setCompetitorRegistrationsInRegattaLog(leaderboard.getName(),
competitors, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
mapCompetitorsToGPSFixDeviceIds(competitorRegistrationsPanel == null ? competitors
: competitorRegistrationsPanel.getSelectedRegisteredCompetitors());
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Failed to register competitors!");
}
});
}
}
public void setCompetitorRegistrationsPanel(CompetitorRegistrationsPanel competitorRegistrationsPanel) {
this.competitorRegistrationsPanel = competitorRegistrationsPanel;
}
}
private void showCompetitorRegistration() {
new RegattaLogCompetitorRegistrationDialog(regatta.boatClass == null ? null : regatta.boatClass.getName(),
new RegattaLogCompetitorRegistrationAndSelectionDialog(regatta.boatClass == null ? null : regatta.boatClass.getName(),
sailingService, stringMessages, errorReporter, true, leaderboard.getName(),
leaderboard.canBoatsOfCompetitorsChangePerRace,
new CancelImportDialogCallback<Set<CompetitorWithBoatDTO>>() {
@Override
public void ok(final Set<CompetitorWithBoatDTO> competitors) {
if (competitors.isEmpty()) {
Window.alert(stringMessages.importCanceledNoCompetitorAdded());
} else {
sailingService.setCompetitorRegistrationsInRegattaLog(leaderboard.getName(),
competitors, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
mapCompetitorsToGPSFixDeviceIds(competitors);
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Failed to register competitors!");
}
});
}
}
}).show();
leaderboard.canBoatsOfCompetitorsChangePerRace).show();
}
private void mapCompetitorsToGPSFixDeviceIds(final Set<CompetitorWithBoatDTO> mappedCompetitors) {
@@ -172,7 +227,17 @@ public class ExpeditionAllInOneAfterImportHandler {
private final void continueWithMappedDevices() {
List<RegattaNameAndRaceName> racesToStopAndStartTrackingFor = new ArrayList<>();
racesToStopAndStartTrackingFor.add(regattaAndRaceIdentifier);
final List<Triple<String, String, String>> leaderboardRaceColumnFleetNames = new ArrayList<>();
final List<Pair<String, String>> raceNames = new ArrayList<>();
for(Triple<String, String, String> race:raceEntries) {
String raceName = race.getA();
String raceColumnName = race.getB();
String fleetName= race.getC();
racesToStopAndStartTrackingFor.add(new RegattaNameAndRaceName(regattaName, raceName));
leaderboardRaceColumnFleetNames.add(new Triple<>(leaderboard.name, raceColumnName, fleetName));
raceNames.add(new Pair<String, String>(raceName, raceColumnName));
}
sailingService.removeAndUntrackRaces(racesToStopAndStartTrackingFor, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
@@ -181,14 +246,12 @@ public class ExpeditionAllInOneAfterImportHandler {
@Override
public void onSuccess(Void result) {
final List<Triple<String, String, String>> leaderboardRaceColumnFleetNames = new ArrayList<>();
leaderboardRaceColumnFleetNames.add(new Triple<>(leaderboard.name, raceColumnName, fleetName));
sailingService.startRaceLogTracking(leaderboardRaceColumnFleetNames, /* trackWind */ true,
/* TODO correctWindByDeclination */ true, new AsyncCallback<Void>() {
sailingService.startRaceLogTracking(leaderboardRaceColumnFleetNames, /* trackWind */ false,
/* correctWindByDeclination */ true, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
new ExpeditionAllInOneImportResultDialog(event.id, regatta.getName(),
regattaAndRaceIdentifier.getRaceName(), leaderboard.getName(),
raceNames, leaderboard.getName(),
leaderboardGroupName).show();
}
@@ -0,0 +1,195 @@
package com.sap.sailing.gwt.ui.adminconsole;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.dto.ExpeditionAllInOneConstants;
import com.sap.sailing.gwt.common.client.suggestion.BoatClassMasterdataSuggestOracle;
import com.sap.sailing.gwt.common.client.suggestion.RegattaSuggestOracle;
import com.sap.sailing.gwt.ui.adminconsole.resulthandling.ExpeditionDataImportResponse;
import com.sap.sailing.gwt.ui.adminconsole.resulthandling.ExpeditionDataImportResultsDialog;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
/**
* The UI form to upload data for expedition all in one import.
*/
public class ExpeditionAllInOneImportPanel extends Composite implements RegattasDisplayer {
private static final String URL_SAILINGSERVER_EXPEDITION_FULL_IMPORT = "/../../sailingserver/expedition/import";
private final RegattaSuggestOracle regattaOracle;
public ExpeditionAllInOneImportPanel(final StringMessages stringMessages, final SailingServiceAsync sailingService,
final ErrorReporter errorReporter, final RegattaRefresher regattaRefresher) {
final FormPanel formPanel = new FormPanel();
final BusyIndicator busyIndicator = new SimpleBusyIndicator();
final Button uploadButton = new Button(stringMessages.upload());
formPanel.getElement().setAttribute("autocomplete", "off");
uploadButton.addClickHandler(event -> {
uploadButton.setEnabled(false);
busyIndicator.setBusy(true);
formPanel.submit();
});
formPanel.setMethod(FormPanel.METHOD_POST);
formPanel.setEncoding(FormPanel.ENCODING_MULTIPART);
formPanel.setAction(GWT.getHostPageBaseURL() + URL_SAILINGSERVER_EXPEDITION_FULL_IMPORT);
final VerticalPanel contentPanel = new VerticalPanel();
formPanel.add(contentPanel);
final Hidden localeField = new Hidden(ExpeditionAllInOneConstants.REQUEST_PARAMETER_LOCALE, LocaleInfo.getCurrentLocale().getLocaleName());
contentPanel.add(localeField);
final FileUpload fileUpload = new FileUpload();
fileUpload.setName("upload");
contentPanel.add(fileUpload);
final FlowPanel importModePanel = new FlowPanel();
contentPanel.add(importModePanel);
final HorizontalPanel regattaNamePanel = new HorizontalPanel();
regattaNamePanel.setVisible(false);
final HorizontalPanel boatClassPanel = new HorizontalPanel();
contentPanel.add(regattaNamePanel);
final RadioButton newEventImport = new RadioButton(ExpeditionAllInOneConstants.REQUEST_PARAMETER_IMPORT_MODE,
stringMessages.createNewEvent());
newEventImport.setFormValue(ExpeditionAllInOneConstants.ImportMode.NEW_EVENT.name());
newEventImport.setValue(true);
importModePanel.add(newEventImport);
newEventImport.addClickHandler(event -> {
regattaNamePanel.setVisible(false);
boatClassPanel.setVisible(true);
});
final RadioButton newCompetitorImport = new RadioButton(
ExpeditionAllInOneConstants.REQUEST_PARAMETER_IMPORT_MODE, stringMessages.newExpeditionCompetitor());
newCompetitorImport.setFormValue(ExpeditionAllInOneConstants.ImportMode.NEW_COMPETITOR.name());
importModePanel.add(newCompetitorImport);
newCompetitorImport.addClickHandler(event -> {
regattaNamePanel.setVisible(true);
boatClassPanel.setVisible(false);
});
final RadioButton newRaceImport = new RadioButton(ExpeditionAllInOneConstants.REQUEST_PARAMETER_IMPORT_MODE,
stringMessages.newExpeditionRace());
newRaceImport.setFormValue(ExpeditionAllInOneConstants.ImportMode.NEW_RACE.name());
importModePanel.add(newRaceImport);
newRaceImport.addClickHandler(event -> {
regattaNamePanel.setVisible(true);
boatClassPanel.setVisible(false);
});
regattaNamePanel.setSpacing(5);
final Label regattaNameLabel = new Label(stringMessages.regattaName() + ":");
regattaNamePanel.add(regattaNameLabel);
regattaNamePanel.setCellVerticalAlignment(regattaNameLabel, HasVerticalAlignment.ALIGN_MIDDLE);
final TextBox regattaName = new TextBox();
regattaName.setName(ExpeditionAllInOneConstants.REQUEST_PARAMETER_REGATTA_NAME);
regattaOracle = new RegattaSuggestOracle();
final SuggestBox regattaSuggestBox = new SuggestBox(regattaOracle, regattaName);
regattaSuggestBox.getValueBox().getElement().getStyle().setProperty("minWidth", 30, Unit.EM);
regattaSuggestBox.getValueBox().getElement().setAttribute("placeholder", stringMessages.startTypingForSuggestions());
regattaNamePanel.add(regattaSuggestBox);
regattaNamePanel.setCellVerticalAlignment(regattaSuggestBox, HasVerticalAlignment.ALIGN_MIDDLE);
boatClassPanel.setSpacing(5);
contentPanel.add(boatClassPanel);
final Label boatClassLabel = new Label(stringMessages.boatClass() + ":");
boatClassPanel.add(boatClassLabel);
boatClassPanel.setCellVerticalAlignment(boatClassLabel, HasVerticalAlignment.ALIGN_MIDDLE);
final SuggestBox boatClassInput = new SuggestBox(new BoatClassMasterdataSuggestOracle());
boatClassInput.getValueBox().getElement().getStyle().setProperty("minWidth", 30, Unit.EM);
boatClassInput.getValueBox().getElement().setAttribute("placeholder", stringMessages.startTypingForSuggestions());
boatClassInput.getValueBox().setName(ExpeditionAllInOneConstants.REQUEST_PARAMETER_BOAT_CLASS);
boatClassPanel.add(boatClassInput);
boatClassPanel.setCellVerticalAlignment(boatClassInput, HasVerticalAlignment.ALIGN_MIDDLE);
final HorizontalPanel controlPanel = new HorizontalPanel();
controlPanel.setSpacing(5);
controlPanel.add(uploadButton);
controlPanel.add(busyIndicator);
contentPanel.add(controlPanel);
final Runnable validation = () -> {
final String filename = fileUpload.getFilename(), boatClass = boatClassInput.getValue();
final String regattaNameValue = regattaSuggestBox.getValue();
final boolean fileValid = filename != null && !filename.trim().isEmpty();
final boolean isNewEventImport = Boolean.TRUE.equals(newEventImport.getValue());
final boolean boatClassValid = boatClass != null && !boatClass.trim().isEmpty();
final boolean regattaNameValid = regattaNameValue != null && !regattaNameValue.trim().isEmpty();
uploadButton.setEnabled(fileValid && (isNewEventImport ? boatClassValid : regattaNameValid));
};
fileUpload.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
validation.run();
}
});
formPanel.addSubmitCompleteHandler(event -> {
validation.run();
busyIndicator.setBusy(false);
final ExpeditionDataImportResponse response = ExpeditionDataImportResponse.parse(event.getResults());
if (response == null) {
Window.alert(StringMessages.INSTANCE.unexpectedErrorDuringFileImport());
} else if (response.hasEventId()) {
new ExpeditionAllInOneAfterImportHandler(response.getEventId(), response.getRegattaName(),
response.getLeaderboardName(), response.getLeaderboardGroupName(), response.getRaceEntries(), response.getGpsDeviceIds(),
response.getSensorDeviceIds(), response.getSensorFixImporterType(), sailingService,
errorReporter, stringMessages);
regattaRefresher.fillRegattas();
} else {
ExpeditionDataImportResultsDialog.showResults(response);
}
});
regattaSuggestBox.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
@Override
public void onSelection(SelectionEvent<Suggestion> event) {
final String selected = event.getSelectedItem().getReplacementString();
if (selected != null) {
sailingService.getRegattaByName(selected, new AsyncCallback<RegattaDTO>() {
@Override
public void onSuccess(RegattaDTO result) {
validation.run();
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Could not determine Regatta " + selected);
}
});
}
}
});
regattaSuggestBox.addKeyUpHandler(event -> validation.run());
fileUpload.addChangeHandler(event -> validation.run());
boatClassInput.addSelectionHandler(event -> validation.run());
boatClassInput.addKeyUpHandler(event -> validation.run());
validation.run();
initWidget(formPanel);
}
@Override
public void fillRegattas(Iterable<RegattaDTO> regattas) {
regattaOracle.fillRegattas(regattas);
}
}
@@ -1,15 +1,17 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List;
import java.util.UUID;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.race.TrackedRaceCreationResultDialog;
import com.sap.sse.common.Util.Pair;
public class ExpeditionAllInOneImportResultDialog extends TrackedRaceCreationResultDialog {
public ExpeditionAllInOneImportResultDialog(UUID eventId, String regattaName, String raceName,
public ExpeditionAllInOneImportResultDialog(UUID eventId, String regattaName, List<Pair<String, String>> raceEntries,
String leaderboardName, String leaderboardGroupName) {
super(StringMessages.INSTANCE.importFinished(), StringMessages.INSTANCE.importFinishedMessage(), eventId,
regattaName, raceName, leaderboardName, leaderboardGroupName);
regattaName, raceEntries, leaderboardName, leaderboardGroupName);
}
}
@@ -16,7 +16,16 @@ public class RegattaLogCompetitorRegistrationDialog extends AbstractCompetitorRe
public RegattaLogCompetitorRegistrationDialog(String boatClass, SailingServiceAsync sailingService,
StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName, boolean canBoatsOfCompetitorsChangePerRace,
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<Set<CompetitorWithBoatDTO>> callback) {
super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, canBoatsOfCompetitorsChangePerRace, boatClass, stringMessages.save(), /* validator */ null);
this(boatClass, sailingService, stringMessages, errorReporter, editable, leaderboardName,
canBoatsOfCompetitorsChangePerRace, /* validator */ null, callback);
}
public RegattaLogCompetitorRegistrationDialog(String boatClass, SailingServiceAsync sailingService,
StringMessages stringMessages, ErrorReporter errorReporter, boolean editable, String leaderboardName,
boolean canBoatsOfCompetitorsChangePerRace, Validator<Set<CompetitorWithBoatDTO>> validator,
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<Set<CompetitorWithBoatDTO>> callback) {
super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName,
canBoatsOfCompetitorsChangePerRace, boatClass, stringMessages.save(), validator);
}
@Override
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -44,7 +45,6 @@ import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -55,10 +55,7 @@ import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.gwt.common.client.suggestion.BoatClassMasterdataSuggestOracle;
import com.sap.sailing.gwt.ui.adminconsole.WindImportResult.RaceEntry;
import com.sap.sailing.gwt.ui.adminconsole.resulthandling.ExpeditionDataImportResponse;
import com.sap.sailing.gwt.ui.adminconsole.resulthandling.ExpeditionDataImportResultsDialog;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -70,12 +67,11 @@ import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.celltable.BaseCelltable;
import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel;
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
/**
@@ -87,13 +83,10 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
*
*/
public class WindPanel extends FormPanel implements RegattasDisplayer, WindShower {
private static final String WIND_IMPORT_PARAMETER_RACES = "races";
private static final String EXPEDITON_IMPORT_PARAMETER_BOAT_ID = "boatId";
private static final String EXPEDITON_IMPORT_PARAMETER_BOAT_CLASS = "boatClass";
private static final String URL_SAILINGSERVER_EXPEDITION_FULL_IMPORT = "/../../sailingserver/expedition/import";
private static final String URL_SAILINGSERVER_EXPEDITION_IMPORT = "/../../sailingserver/expedition-import";
private static final String URL_SAILINGSERVER_GRIB_IMPORT = "/../../sailingserver/grib-wind-import";
private static final String URL_SAILINGSERVER_NMEA_IMPORT = "/../../sailingserver/nmea-wind-import";
@@ -117,23 +110,28 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
private final ListDataProvider<WindDTO> rawWindFixesDataProvider;
private final CellTable<WindDTO> rawWindFixesTable;
private final VerticalPanel windFixPanel;
/**
* Composite pattern over the {@link RegattasDisplayer} interface. Calls to {@link #fillRegattas(Iterable)}
* will be forwarded to those objects contained in this collection.
*/
private final Set<RegattasDisplayer> containedRegattaDisplayers;
public WindPanel(final SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
ErrorReporter errorReporter, RegattaRefresher regattaRefresher, final StringMessages stringMessages) {
ensureDebugId("WindPanel");
this.sailingService = sailingService;
this.containedRegattaDisplayers = new HashSet<>();
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
windSourcesToExcludeSelectorPanel = new WindSourcesToExcludeSelectorPanel(sailingService, stringMessages, errorReporter);
VerticalPanel mainPanel = new VerticalPanel();
mainPanel.setSize("100%", "100%");
this.setWidget(mainPanel);
trackedRacesListComposite = new TrackedRacesListComposite(null, null, sailingService, errorReporter,
regattaRefresher,
stringMessages, /*multiselection*/true, /* actionButtonsEnabled */ false);
containedRegattaDisplayers.add(trackedRacesListComposite);
trackedRacesListComposite.ensureDebugId("TrackedRacesListComposite");
mainPanel.add(trackedRacesListComposite);
refreshableRaceSelectionModel = (RefreshableMultiSelectionModel<RaceDTO>) trackedRacesListComposite.getSelectionModel();
@@ -143,16 +141,13 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
updateWindDisplay();
}
});
windCaptionPanel = new CaptionPanel(stringMessages.wind());
windCaptionPanel.setVisible(false);
mainPanel.add(windCaptionPanel);
TabPanel tabPanel = new TabPanel();
tabPanel.setAnimationEnabled(true);
windCaptionPanel.add(tabPanel);
tabPanel.setSize("95%", "95%");
windFixesDisplayPanel = new VerticalPanel();
Button addWindFixButton = new Button(stringMessages.actionAddWindData() + "...");
addWindFixButton.addClickHandler(new ClickHandler() {
@@ -176,16 +171,13 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
}
});
windFixesDisplayPanel.add(addWindFixButton);
final VerticalPanel windSourcesPanel = new VerticalPanel();
windSourcesPanel.setSpacing(10);
tabPanel.add(windSourcesPanel, stringMessages.windSourcesUsed());
tabPanel.add(windFixesDisplayPanel, stringMessages.windFixes());
tabPanel.selectTab(0);
raceIsKnownToStartUpwindBox = new CheckBox(stringMessages.raceIsKnownToStartUpwind());
windSourcesPanel.add(raceIsKnownToStartUpwindBox);
windSourcesPanel.add(windSourcesToExcludeSelectorPanel);
raceIsKnownToStartUpwindBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
@@ -193,13 +185,10 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
setRaceIsKnownToStartUpwind();
}
});
windFixPanel = new VerticalPanel();
windSourcesPanel.add(windFixPanel);
windSourceLabel = new Label();
windFixesDisplayPanel.add(windSourceLabel);
// table for the raw wind fixes
removeColumn = new IdentityColumn<WindDTO>(new ActionCell<WindDTO>(stringMessages.remove(), new Delegate<WindDTO>() {
@Override
@@ -252,7 +241,6 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
timeColumn.setSortable(true);
speedInKnotsColumn.setSortable(true);
windDirectionInDegColumn.setSortable(true);
AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
rawWindFixesTable = new BaseCelltable<WindDTO>(/* pageSize */10000, tableRes);
rawWindFixesTable.addColumn(timeColumn, stringMessages.time());
@@ -266,13 +254,14 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
rawWindFixesTable.addColumnSortHandler(columnSortHandler);
rawWindFixesTable.getColumnSortList().push(timeColumn);
windFixesDisplayPanel.add(rawWindFixesTable);
mainPanel.add(createExpeditionWindImportPanel());
mainPanel.add(createGribWindImportPanel());
mainPanel.add(createNmeaWindImportPanel());
mainPanel.add(createBravoWindImportPanel());
mainPanel.add(createIgtimiWindImportPanel(mainPanel));
mainPanel.add(createExpeditionAllInOneImportPanel());
final Pair<CaptionPanel, ExpeditionAllInOneImportPanel> expeditionAllInOneRootAndImportPanel = createExpeditionAllInOneImportPanel(regattaRefresher);
mainPanel.add(expeditionAllInOneRootAndImportPanel.getA());
containedRegattaDisplayers.add(expeditionAllInOneRootAndImportPanel.getB());
}
private CaptionPanel createIgtimiWindImportPanel(VerticalPanel mainPanel) {
@@ -448,68 +437,11 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
return new WindImportFileUploadForm(form, formContentPanel, fileUpload, submitButton);
}
private CaptionPanel createExpeditionAllInOneImportPanel() {
private Pair<CaptionPanel, ExpeditionAllInOneImportPanel> createExpeditionAllInOneImportPanel(RegattaRefresher regattaRefresher) {
final CaptionPanel rootPanel = new CaptionPanel(stringMessages.importFullExpeditionData());
final FormPanel formPanel = new FormPanel();
final BusyIndicator busyIndicator = new SimpleBusyIndicator();
final Button uploadButton = new Button(stringMessages.upload());
uploadButton.addClickHandler(event -> {
uploadButton.setEnabled(false);
busyIndicator.setBusy(true);
formPanel.submit();
});
formPanel.setMethod(FormPanel.METHOD_POST);
formPanel.setEncoding(FormPanel.ENCODING_MULTIPART);
formPanel.setAction(GWT.getHostPageBaseURL() + URL_SAILINGSERVER_EXPEDITION_FULL_IMPORT);
formPanel.addSubmitCompleteHandler(event -> {
uploadButton.setEnabled(true);
busyIndicator.setBusy(false);
final ExpeditionDataImportResponse response = ExpeditionDataImportResponse.parse(event.getResults());
if (response == null) {
Window.alert(StringMessages.INSTANCE.unexpectedErrorDuringFileImport());
} else if (response.hasEventId()) {
new ExpeditionAllInOneAfterImportHandler(response.getEventId(), response.getRegattaName(),
response.getLeaderboardGroupName(), response.getLeaderboardName(), response.getRaceName(),
response.getRaceColumnName(), response.getFleetName(), response.getGpsDeviceIds(),
response.getSensorDeviceIds(), response.getSensorFixImporterType(), sailingService,
errorReporter, stringMessages);
} else {
ExpeditionDataImportResultsDialog.showResults(response);
}
});
rootPanel.add(formPanel);
final VerticalPanel contentPanel = new VerticalPanel();
formPanel.add(contentPanel);
final FileUpload fileUpload = new FileUpload();
fileUpload.setName("upload");
contentPanel.add(fileUpload);
final HorizontalPanel boatClassPanel = new HorizontalPanel();
boatClassPanel.setSpacing(5);
contentPanel.add(boatClassPanel);
final Label boatClassLabel = new Label(stringMessages.boatClass() + ":");
boatClassPanel.add(boatClassLabel);
boatClassPanel.setCellVerticalAlignment(boatClassLabel, HasVerticalAlignment.ALIGN_MIDDLE);
final SuggestBox boatClassInput = new SuggestBox(new BoatClassMasterdataSuggestOracle());
boatClassInput.getValueBox().setName(EXPEDITON_IMPORT_PARAMETER_BOAT_CLASS);
boatClassPanel.add(boatClassInput);
boatClassPanel.setCellVerticalAlignment(boatClassInput, HasVerticalAlignment.ALIGN_MIDDLE);
final HorizontalPanel controlPanel = new HorizontalPanel();
controlPanel.setSpacing(5);
controlPanel.add(uploadButton);
controlPanel.add(busyIndicator);
contentPanel.add(controlPanel);
final Runnable validation = () -> {
final String filename = fileUpload.getFilename(), boatClass = boatClassInput.getValue();
final boolean fileValid = filename != null && !filename.trim().isEmpty();
final boolean boatClassValid = boatClass != null && !boatClass.trim().isEmpty();
uploadButton.setEnabled(fileValid && boatClassValid);
};
fileUpload.addChangeHandler(event -> validation.run());
boatClassInput.addSelectionHandler(event -> validation.run());
boatClassInput.addKeyUpHandler(event -> validation.run());
validation.run();
return rootPanel;
final ExpeditionAllInOneImportPanel expeditionAllInOneImportPanel = new ExpeditionAllInOneImportPanel(stringMessages, sailingService, errorReporter, regattaRefresher);
rootPanel.add(expeditionAllInOneImportPanel);
return new Pair<>(rootPanel, expeditionAllInOneImportPanel);
}
private CaptionPanel createExpeditionWindImportPanel() {
@@ -627,7 +559,9 @@ public class WindPanel extends FormPanel implements RegattasDisplayer, WindShowe
@Override
public void fillRegattas(Iterable<RegattaDTO> result) {
trackedRacesListComposite.fillRegattas(result);
for (final RegattasDisplayer containedRegattaDisplayer : containedRegattaDisplayers) {
containedRegattaDisplayer.fillRegattas(result);
}
}
@Override
@@ -1,8 +1,13 @@
package com.sap.sailing.gwt.ui.adminconsole.resulthandling;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.sap.sse.common.Util.Triple;
public class ExpeditionDataImportResponse extends AbstractDataImportResponse {
public static final ExpeditionDataImportResponse parse(String json) {
@@ -24,22 +29,25 @@ public class ExpeditionDataImportResponse extends AbstractDataImportResponse {
public final native String getLeaderboardName() /*-{
return this.leaderboardName;
}-*/;
public final native String getRegattaName() /*-{
return this.regattaName;
}-*/;
public final native String getRaceName() /*-{
return this.raceName;
}-*/;
public final native String getRaceColumnName() /*-{
return this.raceColumnName;
}-*/;
public final native String getFleetName() /*-{
return this.fleetName;
}-*/;
public final List<Triple<String, String, String>> getRaceEntries() {
ArrayList<Triple<String, String, String>> raceNameRaceColumnNameFleetNameList = new ArrayList<>();
JSONObject jsonView = new JSONObject(this);
JSONArray entryList = jsonView.get("raceNameRaceColumnNameFleetNameList").isArray();
for (int i = 0; i < entryList.size(); i++) {
JSONObject entry = entryList.get(i).isObject();
String raceName = entry.get("raceName").isString().stringValue();
String raceColumnName = entry.get("raceColumnName").isString().stringValue();
String fleetName = entry.get("fleetName").isString().stringValue();
raceNameRaceColumnNameFleetNameList
.add(new Triple<String, String, String>(raceName, raceColumnName, fleetName));
}
return raceNameRaceColumnNameFleetNameList;
}
public final List<String> getGpsDeviceIds() {
return getStringList("gpsDeviceIds");
@@ -28,13 +28,14 @@ import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.ServiceException;
import com.sap.sailing.domain.common.UnableToCloseDeviceMappingException;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog;
import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO;
import com.sap.sailing.domain.common.dto.PairingListDTO;
@@ -764,8 +765,9 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
/**
* Slices a new race from the race specified by the given {@link RegattaAndRaceIdentifier} using the given time range.
* A new {@link RaceColumn} with the given name is added to the {@link RegattaLeaderboard}.
* @throws ServiceException
*/
RegattaAndRaceIdentifier sliceRace(RegattaAndRaceIdentifier raceIdentifier, String newRaceColumnName, TimePoint sliceFrom, TimePoint sliceTo);
RegattaAndRaceIdentifier sliceRace(RegattaAndRaceIdentifier raceIdentifier, String newRaceColumnName, TimePoint sliceFrom, TimePoint sliceTo) throws ServiceException;
/**
* Returns specific data needed for the slicing UI.
@@ -2071,6 +2071,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String importFinished();
String importFinishedMessage();
String importFinishedGotoRaceboard();
String importFinishedGotoRaceboardWithName(String name);
String importFinishedGotoEvent();
String chooseAName();
String errorFillingRaceLogsFromPairingList(String message);
@@ -2148,4 +2149,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String expeditionRaceJibCarStbd();
String expeditionRaceMastButt();
String milliBarUnits();
String newExpeditionRace();
String newExpeditionCompetitor();
String selectOneCompetitorToMapTheImportedData();
String startTypingForSuggestions();
}
@@ -2066,6 +2066,7 @@ importCanceledByUser=Import canceled!
importFinished=Finished importing data
importFinishedMessage=Successfully imported expedition data.
importFinishedGotoRaceboard=Open raceboard
importFinishedGotoRaceboardWithName=Open raceboard for "{0}"
importFinishedGotoEvent=Open event
chooseAName=Choose a name
errorFillingRaceLogsFromPairingList=Error filling race logs from pairing list: {0}
@@ -2138,3 +2139,7 @@ expeditionRaceJibCarPort=eaJibCarPFromBow
expeditionRaceJibCarStbd=eaJibCarSFromBow
expeditionRaceMastButt=exMastButt
milliBarUnits=mbar
newExpeditionRace=Create a new race
newExpeditionCompetitor=Add a new Competitor
selectOneCompetitorToMapTheImportedData=Please select one competitor on the left side. The imported data will be mapped to this competitor.
startTypingForSuggestions=Start typing to get suggestions
@@ -2063,6 +2063,7 @@ importCanceledByUser=Import abgebrochen!
importFinished=Importvorgang abgeschlossen
importFinishedMessage=Erfolgreich Expeditionsdaten eingelesen
importFinishedGotoRaceboard=Raceboard öffnen
importFinishedGotoRaceboardWithName=Raceboard für "{0}" öffnen
importFinishedGotoEvent=Event öffnen
chooseAName=Namen auswählen
errorFillingRaceLogsFromPairingList=Fehler beim Befüllen der Rennprotokolle aus der Gegner-Paarungsliste: {0}
@@ -2134,3 +2135,7 @@ expeditionRaceLoadP=eFShroudP
expeditionRaceJibCarPort=eaJibCarPFromBow
expeditionRaceJibCarStbd=eaJibCarSFromBow
expeditionRaceMastButt=exMastButt
newExpeditionRace=Neues Rennen anlegen
newExpeditionCompetitor=Neuen Teilnehmer hinzufügen
selectOneCompetitorToMapTheImportedData=Bitte einen Teilnehmer auf der linken Seite auswählen. Die importierten Daten werden dem ausgewählten Teinehmer zugeordnet.
startTypingForSuggestions=Tippen starten, um Vorschlagsliste zuerhalten
@@ -1,6 +1,8 @@
package com.sap.sailing.gwt.ui.client.shared.race;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import com.google.gwt.user.client.ui.Anchor;
@@ -9,21 +11,32 @@ import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.settings.client.EntryPointWithSettingsLinkFactory;
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
/**
* Summary dialog to be shown after actions that generate a new TrackedRace.
* the dialog shows a link to the RaceBoard for the generated TrackedRace.
* In addition, if available a link to the associated event is shown.
* Summary dialog to be shown after actions that generate a new TrackedRace. the dialog shows a link to the RaceBoard
* for the generated TrackedRace. In addition, if available a link to the associated event is shown.
*/
public class TrackedRaceCreationResultDialog extends DataEntryDialog<Void> {
private final VerticalPanel verticalPanel;
public TrackedRaceCreationResultDialog(String title, String message, UUID eventId, String regattaName, String raceName,
String leaderboardName, String leaderboardGroupName) {
super(title, message,
StringMessages.INSTANCE.ok(), StringMessages.INSTANCE.cancel(), /* validator */ null,
public TrackedRaceCreationResultDialog(String title, String message, UUID eventId, String regattaName,
String raceName, String leaderboardName, String leaderboardGroupName) {
this(title, message, eventId, regattaName, createSingleRaceList(raceName), leaderboardName,
leaderboardGroupName);
}
private static List<Pair<String, String>> createSingleRaceList(String raceName) {
ArrayList<Pair<String, String>> rv = new ArrayList<>();
rv.add(new Pair<String, String>(raceName, null));
return rv;
}
public TrackedRaceCreationResultDialog(String title, String message, UUID eventId, String regattaName,
List<Pair<String, String>> raceNameRaceColumnName, String leaderboardName, String leaderboardGroupName) {
super(title, message, StringMessages.INSTANCE.ok(), StringMessages.INSTANCE.cancel(), /* validator */ null,
new DialogCallback<Void>() {
@Override
public void ok(Void editedObject) {
@@ -36,19 +49,24 @@ public class TrackedRaceCreationResultDialog extends DataEntryDialog<Void> {
verticalPanel = new VerticalPanel();
verticalPanel.setSpacing(20);
Anchor raceboardAnchor = new Anchor(StringMessages.INSTANCE.importFinishedGotoRaceboard(),
EntryPointWithSettingsLinkFactory.createRaceBoardLinkWithDefaultSettings(eventId, leaderboardName,
leaderboardGroupName, regattaName, raceName));
raceboardAnchor.setTarget("_blank");
verticalPanel.add(raceboardAnchor);
boolean multiRace = raceNameRaceColumnName.size() > 1;
for (Pair<String, String> raceName : raceNameRaceColumnName) {
String anchorLabel = multiRace ? StringMessages.INSTANCE.importFinishedGotoRaceboardWithName(raceName.getB())
: StringMessages.INSTANCE.importFinishedGotoRaceboard();
Anchor raceboardAnchor = new Anchor(anchorLabel,
EntryPointWithSettingsLinkFactory.createRaceBoardLinkWithDefaultSettings(eventId, leaderboardName,
leaderboardGroupName, regattaName, raceName.getA()));
raceboardAnchor.setTarget("_blank");
verticalPanel.add(raceboardAnchor);
}
if (eventId != null) {
Anchor eventAnchor = new Anchor(StringMessages.INSTANCE.importFinishedGotoEvent(),
EntryPointLinkFactory.createEventPlaceLink(eventId.toString(), Collections.emptyMap()));
eventAnchor.setTarget("_blank");
verticalPanel.add(eventAnchor);
}
}
@Override
@@ -147,8 +147,9 @@ public class SliceRaceHandler {
@Override
public void onFailure(Throwable caught) {
errorReporter
.reportError(StringMessages.INSTANCE.errorWhileSlicingARace());
errorReporter.reportError(
StringMessages.INSTANCE.errorWhileSlicingARace(),
caught.getMessage());
}
@Override
@@ -17,6 +17,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -224,6 +225,7 @@ import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionForC
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace;
import com.sap.sailing.domain.common.ScoreCorrectionProvider;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.ServiceException;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
@@ -546,6 +548,7 @@ import com.sap.sse.gwt.shared.replication.ReplicaDTO;
import com.sap.sse.gwt.shared.replication.ReplicationMasterDTO;
import com.sap.sse.gwt.shared.replication.ReplicationStateDTO;
import com.sap.sse.i18n.ResourceBundleStringMessages;
import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
import com.sap.sse.pairinglist.PairingList;
import com.sap.sse.pairinglist.PairingListTemplate;
import com.sap.sse.pairinglist.impl.PairingListTemplateImpl;
@@ -574,6 +577,8 @@ import com.sapsailing.xrr.structureimport.eventimport.RegattaJSON;
public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements SailingService, RaceFetcher, RegattaFetcher {
private static final Logger logger = Logger.getLogger(SailingServiceImpl.class.getName());
private static final String STRING_MESSAGES_BASE_NAME = "stringmessages/StringMessages";
private static final long serialVersionUID = 9031688830194537489L;
private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
@@ -623,6 +628,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private static final int LEADERBOARD_DIFFERENCE_CACHE_SIZE = 50;
private ResourceBundleStringMessages serverStringMessages;
private final LinkedHashMap<String, LeaderboardDTO> leaderboardByNameResultsCacheById;
@@ -693,6 +699,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
// providing the updates is not outperformed by all the re-calculations happening here. Leave at least one
// core to other things, but by using at least three threads ensure that no simplistic deadlocks may occur.
executor = ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor();
serverStringMessages = new ResourceBundleStringMessagesImpl(STRING_MESSAGES_BASE_NAME,
this.getClass().getClassLoader(), StandardCharsets.UTF_8.name());
}
/**
@@ -7496,23 +7505,25 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public RegattaAndRaceIdentifier sliceRace(RegattaAndRaceIdentifier raceIdentifier, String newRaceColumnName,
TimePoint sliceFrom, TimePoint sliceTo) {
TimePoint sliceFrom, TimePoint sliceTo) throws ServiceException {
final Locale locale = getClientLocale();
SecurityUtils.getSubject().checkPermission(Permission.MANAGE_TRACKED_RACES.getStringPermissionForObjects(Mode.UPDATE));
if (!canSliceRace(raceIdentifier)) {
throw new RuntimeException("Can not slice race");
throw new ServiceException(serverStringMessages.get(locale, "slicingCannotSliceRace"));
}
final String trackedRaceName = newRaceColumnName;
final RegattaIdentifier regattaIdentifier = new RegattaName(raceIdentifier.getRegattaName());
final Regatta regatta = getService().getRegatta(regattaIdentifier);
if (regatta.getRaceColumnByName(newRaceColumnName) != null) {
throw new RuntimeException("The race column name is already used in the given regatta");
throw new ServiceException(serverStringMessages.get(locale, "slicingRaceColumnAlreadyUsedThe"));
}
final DynamicTrackedRace trackedRaceToSlice = getService().getTrackedRace(raceIdentifier);
final TimePoint startOfTrackingOfRaceToSlice = trackedRaceToSlice.getStartOfTracking();
final TimePoint endOfTrackingOfRaceToSlice = trackedRaceToSlice.getEndOfTracking();
if (sliceFrom == null || sliceTo == null || startOfTrackingOfRaceToSlice.after(sliceFrom)
|| (endOfTrackingOfRaceToSlice != null && endOfTrackingOfRaceToSlice.before(sliceTo))) {
throw new RuntimeException("The TimeRange to slice is not part of the race");
throw new ServiceException(serverStringMessages.get(locale, "slicingTimeRangeOutOfBounds"));
}
final RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) getService().getLeaderboardByName(raceIdentifier.getRegattaName());
final Pair<RaceColumn, Fleet> raceColumnAndFleetOfRaceToSlice = regattaLeaderboard.getRaceColumnAndFleet(trackedRaceToSlice);
@@ -7743,7 +7754,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
final DynamicTrackedRace trackedRace = WaitForTrackedRaceUtil.waitForTrackedRace(raceColumn, fleet, 10);
if (trackedRace == null) {
throw new IllegalStateException("Could not obtain sliced race");
throw new ServiceException(serverStringMessages.get(locale, "slicingCouldNotObtainRace"));
}
for (WindSource windSourceToCopy : trackedRaceToSlice.getWindSources()) {
if (windSourceToCopy.canBeStored()) {
@@ -7774,7 +7785,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
return trackedRace.getRaceIdentifier();
} catch (Exception e) {
throw new RuntimeException("Error while slicing race", e);
throw new ServiceException(serverStringMessages.get(locale, "slicingError"));
}
}
@@ -0,0 +1,5 @@
slicingCannotSliceRace=Can not slice race
slicingRaceColumnAlreadyUsedThe=Race column name is already used in the given regatta
slicingTimeRangeOutOfBounds=The TimeRange to slice is not part of the race
slicingCouldNotObtainRace=Could not obtain sliced race
slicingError=Error slicing race
@@ -0,0 +1,5 @@
slicingCannotSliceRace=Rennen kann nicht geschnitten werden
slicingRaceColumnAlreadyUsedThe=Der Rennspaltenname ist in der Regatta bereits verwendet
slicingTimeRangeOutOfBounds=Die angegebene Zeitspanne ist ausserhalb des Rennens
slicingCouldNotObtainRace=Das geschnittene Rennen konnte nicht gefunden werden
slicingError=Fehler beim Schneiden des Rennens
@@ -9,10 +9,10 @@ import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer;
import com.sap.sse.common.Color;
import com.sap.sse.common.impl.AbstractColor;
import com.sap.sse.util.impl.UUIDHelper;
/**
* Deserializer for marks.
@@ -27,7 +27,7 @@ public class MarkDeserializer implements JsonDeserializer<Mark> {
@Override
public Mark deserialize(JSONObject object) throws JsonDeserializationException {
Serializable id = Helpers.tryUuidConversion((Serializable) object.get(MarkJsonSerializer.FIELD_ID));
Serializable id = UUIDHelper.tryUuidConversion((Serializable) object.get(MarkJsonSerializer.FIELD_ID));
String colorAsString = (String) object.get(MarkJsonSerializer.FIELD_COLOR);
Color color = colorAsString == null ? null : AbstractColor.getCssColor(colorAsString);
String pattern = (String) object.get(MarkJsonSerializer.FIELD_PATTERN);
@@ -16,6 +16,7 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer;
import com.sap.sse.common.Color;
import com.sap.sse.common.impl.RGBColor;
import com.sap.sse.util.impl.UUIDHelper;
public class BoatJsonDeserializer implements JsonDeserializer<DynamicBoat> {
private static final Logger logger = Logger.getLogger(BoatJsonDeserializer.class.getName());
@@ -63,7 +64,7 @@ public class BoatJsonDeserializer implements JsonDeserializer<DynamicBoat> {
Constructor<?> constructorFromString = idClass.getConstructor(String.class);
boatId = (Serializable) constructorFromString.newInstance(boatIdFromJson.toString());
} else if (UUID.class.isAssignableFrom(idClass)) {
boatId = Helpers.tryUuidConversion(boatIdFromJson);
boatId = UUIDHelper.tryUuidConversion(boatIdFromJson);
} else {
boatId = boatIdFromJson;
}
@@ -21,6 +21,7 @@ import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerialize
import com.sap.sse.common.Color;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.RGBColor;
import com.sap.sse.util.impl.UUIDHelper;
public class CompetitorJsonDeserializer implements JsonDeserializer<DynamicCompetitor> {
private final CompetitorFactory competitorWithBoatFactory;
@@ -52,7 +53,7 @@ public class CompetitorJsonDeserializer implements JsonDeserializer<DynamicCompe
Constructor<?> constructorFromString = idClass.getConstructor(String.class);
competitorId = (Serializable) constructorFromString.newInstance(competitorId.toString());
} else if (UUID.class.isAssignableFrom(idClass)) {
competitorId = Helpers.tryUuidConversion(competitorId);
competitorId = UUIDHelper.tryUuidConversion(competitorId);
}
String name = (String) object.get(CompetitorJsonConstants.FIELD_NAME);
String shortName = (String) object.get(CompetitorJsonConstants.FIELD_SHORT_NAME);
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.common.tracking.impl.CompetitorJsonConstants;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sse.util.impl.UUIDHelper;
public class CompetitorWithBoatRefJsonDeserializer extends CompetitorJsonDeserializer {
private final BoatFactory boatFactory;
@@ -47,7 +48,7 @@ public class CompetitorWithBoatRefJsonDeserializer extends CompetitorJsonDeseria
Constructor<?> constructorFromString = boatIdClass.getConstructor(String.class);
boatId = (Serializable) constructorFromString.newInstance(boatId.toString());
} else if (UUID.class.isAssignableFrom(boatIdClass)) {
boatId = Helpers.tryUuidConversion(boatId);
boatId = UUIDHelper.tryUuidConversion(boatId);
}
}
final DynamicBoat existingBoat = (DynamicBoat) (boatId != null ? boatFactory.getExistingBoatById(boatId) : null);
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer;
import com.sap.sse.util.impl.UUIDHelper;
public class CourseAreaJsonDeserializer implements JsonDeserializer<CourseArea> {
@@ -24,7 +25,7 @@ public class CourseAreaJsonDeserializer implements JsonDeserializer<CourseArea>
String name = (String) object.get(CourseAreaJsonSerializer.FIELD_NAME);
Serializable id = (Serializable) object.get(CourseAreaJsonSerializer.FIELD_ID);
return factory.getOrCreateCourseArea((UUID) Helpers.tryUuidConversion(id), name);
return factory.getOrCreateCourseArea((UUID) UUIDHelper.tryUuidConversion(id), name);
}
}
@@ -1,9 +1,7 @@
package com.sap.sailing.server.gateway.deserialization.impl;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;
import java.util.logging.Logger;
import org.json.simple.JSONArray;
@@ -12,7 +10,6 @@ import org.json.simple.JSONObject;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
public class Helpers {
private final static Logger logger = Logger.getLogger(Helpers.class.getName());
public static JSONArray toJSONArraySafe(Object object) throws JsonDeserializationException {
@@ -62,19 +59,4 @@ public class Helpers {
}
return result;
}
/**
* If the {@link Object#toString() string representation} of {@code serializableId} {@link UUID#fromString(String) parses}
* as a {@link UUID}, the resulting {@link UUID} is returned. Otherwise, the {@code serializableId} parameter's value
* is returned unchanged.
*/
public static Serializable tryUuidConversion(Serializable serializableId) {
try {
return UUID.fromString(serializableId.toString());
} catch (IllegalArgumentException iae) {
logger.warning("The serializable " + serializableId.toString() + " could not be converted to a UUID");
}
return serializableId;
}
}
@@ -16,6 +16,7 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer;
import com.sap.sse.common.Color;
import com.sap.sse.common.impl.RGBColor;
import com.sap.sse.util.impl.UUIDHelper;
public class LegacyBoatJsonDeserializer implements JsonDeserializer<DynamicBoat> {
private final BoatClassJsonDeserializer boatClassDeserializer;
@@ -41,7 +42,7 @@ public class LegacyBoatJsonDeserializer implements JsonDeserializer<DynamicBoat>
Constructor<?> constructorFromString = idClass.getConstructor(String.class);
boatId = (Serializable) constructorFromString.newInstance(boatId.toString());
} else if (UUID.class.isAssignableFrom(idClass)) {
boatId = Helpers.tryUuidConversion(boatId);
boatId = UUIDHelper.tryUuidConversion(boatId);
}
}
String name = (String) object.get(BoatJsonSerializer.FIELD_NAME);
@@ -21,6 +21,7 @@ import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerialize
import com.sap.sse.common.Color;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.RGBColor;
import com.sap.sse.util.impl.UUIDHelper;
public class LegacyCompetitorWithContainedBoatJsonDeserializer implements JsonDeserializer<CompetitorWithBoat> {
protected final CompetitorFactory competitorWithBoatFactory;
@@ -52,7 +53,7 @@ public class LegacyCompetitorWithContainedBoatJsonDeserializer implements JsonDe
Constructor<?> constructorFromString = idClass.getConstructor(String.class);
competitorId = (Serializable) constructorFromString.newInstance(competitorId.toString());
} else if (UUID.class.isAssignableFrom(idClass)) {
competitorId = Helpers.tryUuidConversion(competitorId);
competitorId = UUIDHelper.tryUuidConversion(competitorId);
}
String name = (String) object.get(CompetitorJsonConstants.FIELD_NAME);
String shortName = (String) object.get(CompetitorJsonConstants.FIELD_SHORT_NAME);
@@ -18,6 +18,7 @@ import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
import com.sap.sailing.server.gateway.serialization.racelog.impl.BaseRaceLogEventSerializer;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.impl.UUIDHelper;
public abstract class BaseRaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent> {
@@ -55,7 +56,7 @@ public abstract class BaseRaceLogEventDeserializer implements JsonDeserializer<R
}
return deserialize(
object,
Helpers.tryUuidConversion(id),
UUIDHelper.tryUuidConversion(id),
new MillisecondsTimePoint(createdAt.longValue()),
author,
timeStamp == null ? null : new MillisecondsTimePoint(timeStamp.longValue()),
@@ -22,6 +22,7 @@ import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogFinishPositioningConfirmedEventSerializer;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.impl.UUIDHelper;
public abstract class RaceLogFinishPositioningEventDeserializer extends BaseRaceLogEventDeserializer {
@@ -47,7 +48,7 @@ public abstract class RaceLogFinishPositioningEventDeserializer extends BaseRace
for (Object object : jsonPositionedCompetitors) {
JSONObject jsonPositionedCompetitor = Helpers.toJSONObjectSafe(object);
Serializable competitorId = (Serializable) jsonPositionedCompetitor.get(RaceLogFinishPositioningConfirmedEventSerializer.FIELD_COMPETITOR_ID);
competitorId = Helpers.tryUuidConversion(competitorId);
competitorId = UUIDHelper.tryUuidConversion(competitorId);
final String competitorDisplayName = (String) jsonPositionedCompetitor.get(RaceLogFinishPositioningConfirmedEventSerializer.FIELD_COMPETITOR_NAME);
final String maxPointsReasonName = (String) jsonPositionedCompetitor.get(RaceLogFinishPositioningConfirmedEventSerializer.FIELD_SCORE_CORRECTIONS_MAX_POINTS_REASON);
final MaxPointsReason maxPointsReason = MaxPointsReason.valueOf(maxPointsReasonName);
@@ -12,9 +12,9 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogRevokeEventSerializer;
import com.sap.sse.common.TimePoint;
import com.sap.sse.util.impl.UUIDHelper;
public class RaceLogRevokeEventDeserializer extends BaseRaceLogEventDeserializer {
public RaceLogRevokeEventDeserializer(JsonDeserializer<DynamicCompetitor> competitorDeserializer) {
@@ -24,7 +24,7 @@ public class RaceLogRevokeEventDeserializer extends BaseRaceLogEventDeserializer
@Override
protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt, AbstractLogEventAuthor author, TimePoint timePoint, int passId, List<Competitor> competitors)
throws JsonDeserializationException {
Serializable revokedEventId = Helpers.tryUuidConversion(
Serializable revokedEventId = UUIDHelper.tryUuidConversion(
(Serializable) object.get(RaceLogRevokeEventSerializer.FIELD_REVOKED_EVENT_ID));
String revokedEventType = (String) object.get(RaceLogRevokeEventSerializer.FIELD_REVOKED_EVENT_TYPE);
String revokedEventShortInfo = (String) object.get(RaceLogRevokeEventSerializer.FIELD_REVOKED_EVENT_SHORT_INFO);
@@ -16,6 +16,7 @@ import com.sap.sailing.server.gateway.deserialization.TypeBasedJsonDeserializer;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
import com.sap.sse.common.TypeBasedServiceFinder;
import com.sap.sse.common.Util;
import com.sap.sse.util.impl.UUIDHelper;
public class DeviceAndSessionIdentifierWithGPSFixesDeserializer
implements JsonDeserializer<Util.Triple<DeviceIdentifier, Serializable, List<GPSFix>>> {
@@ -41,7 +42,7 @@ implements JsonDeserializer<Util.Triple<DeviceIdentifier, Serializable, List<GPS
Object sessionObject = object.get(FIELD_SESSION_UUID);
Serializable sessionId = sessionObject == null ?
null : Helpers.tryUuidConversion(((Serializable) sessionObject));
null : UUIDHelper.tryUuidConversion(((Serializable) sessionObject));
JSONArray fixesJson = Helpers.getNestedArraySafe(object, FIELD_FIXES);
List<GPSFix> fixes = new ArrayList<GPSFix>();
@@ -0,0 +1,16 @@
allInOneErrorImportFileMissing=No file to import found!
allInOneErrorUnknownImportMode=Unknown import mode!
allInOneErrorMissingBoatClass=No boat class name found!
allInOneErrorMissingRegattaClass=No regatta name found!
allInOneErrorGPSDataImportFailed=Failed to import GPS data!
allInOneErrorInvalidData=The regatta could not be resolved, please ensure the name is correct
allInOneErrorInvalidRegattaName=Please enter a valid regatta name
allInOneErrorInvalidImportMode=Not handled ImportMode
allInOneErrorMultiSeries=There is more than one series in this regatta, cannot add race
allInOneErrorInvalidSeries=There is no series in this regatta, cannot add race
allInOneErrorSplitFleetNotSupported=The expedition importer cannot handle split fleet racing
allInOneErrorInvalidRace=To add competitors, a race must be existing
allInOneErrorInvalidLeaderBoardEventLink=The Event for the leaderboard could not be obtained, please ensure the leaderboard is properly attached to an leaderboard group that is attached to an event
allInOneErrorInvalidLeaderBoard=The Leaderboard could not be resolved, please ensure a leaderboard for the regatta exists
allInOneErrorSensorDataImportFailed=Failed to import sensor data!
allInOneErrorBoatClassDeterminationFailed=Failed to determine boat class!
@@ -0,0 +1,16 @@
allInOneErrorImportFileMissing=Die Importdatei wurde nicht gefunden!
allInOneErrorUnknownImportMode=Unbekannter Importmodus!
allInOneErrorMissingBoatClass=Keine Bootsklasse gefunden!
allInOneErrorMissingRegattaClass=Kein Regattaname gefunden!
allInOneErrorGPSDataImportFailed=Fehler beim Import der GPS-Daten!
allInOneErrorInvalidData=Die Regatta konnte nicht gefunden werden. Bitte sicherstellen, dass der Name gültig ist.
allInOneErrorInvalidRegattaName=Bitte einen gültigen Regattanamen eingeben
allInOneErrorInvalidImportMode=Nicht unterstützter Importmodus
allInOneErrorMultiSeries=Es existiert mehr als eine Serie in der Regatta, Rennen kann nicht hinzugefügt werden
allInOneErrorInvalidSeries=Es existiert keine Serie in der Regatta, Rennen kann nicht hinzugefügt werden
allInOneErrorSplitFleetNotSupported=Der Expeditionimporter kann nicht in Regatten mit mehreren Flotten importieren
allInOneErrorInvalidRace=Um Teilnehmer hinzuzufügen, muss ein Rennen existieren
allInOneErrorInvalidLeaderBoardEventLink=Das Event welches zu der Rangliste gehört konnnte nicht bestimmt werden. Bitte sicherstellen, dass die Rangliste an einem Event registiert ist.
allInOneErrorInvalidLeaderBoard=Die Rangliste konnte nicht aufgelöst werden. Bitte sicherstellen, dass eine Rangliste für die Regatta existiert.
allInOneErrorSensorDataImportFailed=Fehler beim Import der Sensor-Daten!
allInOneErrorBoatClassDeterminationFailed=Fehler beim Bestimmen der Bootsklasse
@@ -124,6 +124,7 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.impl.UUIDHelper;
@Path("/v1/leaderboards")
public class LeaderboardsResource extends AbstractLeaderboardsResource {
@@ -322,7 +323,7 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
from, /* to */ null);
} else {
// map to a mark
final Mark mappedToMark = domainFactory.getExistingMarkById(Helpers.tryUuidConversion(markId));
final Mark mappedToMark = domainFactory.getExistingMarkById(UUIDHelper.tryUuidConversion(markId));
mappedTo = mappedToMark;
if (mappedToMark == null) {
logger.warning("No mark found for id " + markId);
@@ -396,7 +397,7 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
} else {
// map to mark
DomainFactory domainFactory = getService().getDomainObjectFactory().getBaseDomainFactory();
final Mark mappedToMark = domainFactory.getExistingMarkById(Helpers.tryUuidConversion(markId));
final Mark mappedToMark = domainFactory.getExistingMarkById(UUIDHelper.tryUuidConversion(markId));
mappedTo = mappedToMark;
if (mappedToMark == null) {
logger.warning("No mark found for id " + markId);
@@ -1,8 +1,10 @@
package com.sap.sailing.server.gateway.trackfiles.impl;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -15,30 +17,37 @@ import org.apache.commons.fileupload.FileItem;
import org.json.simple.JSONObject;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.common.dto.ExpeditionAllInOneConstants;
import com.sap.sailing.domain.common.dto.ExpeditionAllInOneConstants.ImportMode;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.server.gateway.impl.AbstractFileUploadServlet;
import com.sap.sailing.server.gateway.trackfiles.impl.ExpeditionAllInOneImporter.ImporterResult;
import com.sap.sse.i18n.ResourceBundleStringMessages;
import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
import com.sap.sse.util.ServiceTrackerFactory;
/**
* Import servlet for sensor data files. Importers are located through the OSGi service registry and matched against the
* name provided by the upload form.
*
* @see ExpeditionAllInOneImporter
*/
public class ExpeditionAllInOneImportServlet extends AbstractFileUploadServlet {
private static final long serialVersionUID = 1120226743039934620L;
private static final Logger logger = Logger.getLogger(ExpeditionAllInOneImportServlet.class.getName());
private static final String REQUEST_PARAMETER_BOAT_CLASS = "boatClass";
private static final String ERROR_MESSAGE_IMPORT_FILE_MISSING = "No file to import found!";
private static final String ERROR_MESSAGE_BOAT_CLASS_MISSING = "No boat class name found!";
private static final String STRING_MESSAGES_BASE_NAME = "stringmessages/StringMessages";
private ServiceTracker<RaceLogTrackingAdapterFactory, RaceLogTrackingAdapterFactory> raceLogTrackingAdapterTracker;
private ResourceBundleStringMessages serverStringMessages;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
raceLogTrackingAdapterTracker = ServiceTrackerFactory.createAndOpen(getContext(),
RaceLogTrackingAdapterFactory.class);
serverStringMessages = new ResourceBundleStringMessagesImpl(STRING_MESSAGES_BASE_NAME,
this.getClass().getClassLoader(), StandardCharsets.UTF_8.name());
}
/**
@@ -53,25 +62,63 @@ public class ExpeditionAllInOneImportServlet extends AbstractFileUploadServlet {
String fileName = null;
FileItem fileItem = null;
String boatClassName = null;
String regattaName = null;
String importModeName = null;
String localeName = null;
for (FileItem fi : fileItems) {
if (!fi.isFormField()) {
fileName = fi.getName();
fileItem = fi;
} else if (fi.getFieldName() != null) {
if (REQUEST_PARAMETER_BOAT_CLASS.equals(fi.getFieldName())) {
if (ExpeditionAllInOneConstants.REQUEST_PARAMETER_BOAT_CLASS.equals(fi.getFieldName())) {
boatClassName = fi.getString();
}
if (ExpeditionAllInOneConstants.REQUEST_PARAMETER_REGATTA_NAME.equals(fi.getFieldName())) {
regattaName = fi.getString();
}
if (ExpeditionAllInOneConstants.REQUEST_PARAMETER_IMPORT_MODE.equals(fi.getFieldName())) {
importModeName = fi.getString();
}
if (ExpeditionAllInOneConstants.REQUEST_PARAMETER_LOCALE.equals(fi.getFieldName())) {
localeName = fi.getString();
}
}
}
Locale uiLocale;
if (localeName == null) {
uiLocale = Locale.ENGLISH;
} else {
try {
uiLocale = Locale.forLanguageTag(localeName);
} catch (Exception e) {
uiLocale = Locale.ENGLISH;
}
}
if (fileItem == null) {
throw new AllinOneImportException(ERROR_MESSAGE_IMPORT_FILE_MISSING);
throw new AllinOneImportException(serverStringMessages.get(uiLocale, "allInOneErrorImportFileMissing"));
}
if (boatClassName == null || boatClassName.isEmpty()) {
throw new AllinOneImportException(ERROR_MESSAGE_BOAT_CLASS_MISSING);
final ImportMode importMode;
if (importModeName == null) {
importMode = ImportMode.NEW_EVENT;
} else {
try {
importMode = ImportMode.valueOf(importModeName);
} catch (Exception e) {
throw new AllinOneImportException(serverStringMessages.get(uiLocale, "allInOneErrorUnknownImportMode"));
}
}
importerResult = new ExpeditionAllInOneImporter(getService(),
if (importMode == ImportMode.NEW_EVENT) {
if (boatClassName == null || boatClassName.isEmpty()) {
throw new AllinOneImportException(serverStringMessages.get(uiLocale, "allInOneErrorMissingBoatClass"));
}
} else {
if (regattaName == null || regattaName.isEmpty()) {
throw new AllinOneImportException(serverStringMessages.get(uiLocale, "allInOneErrorMissingRegattaClass"));
}
}
importerResult = new ExpeditionAllInOneImporter(serverStringMessages, uiLocale, getService(),
raceLogTrackingAdapterTracker.getService().getAdapter(getService().getBaseDomainFactory()),
getServiceFinderFactory(), getContext()).importFiles(fileName, fileItem, boatClassName);
getServiceFinderFactory(), getContext()).importFiles(fileName, fileItem, boatClassName, importMode, regattaName);
} catch (AllinOneImportException e) {
importerResult = new ImporterResult(e, e.additionalErrors);
logger.log(Level.SEVERE, e.getMessage());
@@ -89,9 +136,7 @@ public class ExpeditionAllInOneImportServlet extends AbstractFileUploadServlet {
json.put("leaderboardName", importerResult.leaderboardName);
json.put("leaderboardGroupName", importerResult.leaderboardGroupName);
json.put("regattaName", importerResult.regattaName);
json.put("raceName", importerResult.raceName);
json.put("raceColumnName", importerResult.raceColumnName);
json.put("fleetName", importerResult.fleetName);
json.put("raceNameRaceColumnNameFleetNameList", ImportResultSerializer.serializeRaceList(importerResult.raceNameRaceColumnNameFleetnameList));
json.put("errors", ImportResultSerializer.serializeErrorList(importerResult.errorList));
json.put("gpsDeviceIds", ImportResultSerializer.serializeTrackList(importerResult.importGpsFixData));
json.put("sensorDeviceIds", ImportResultSerializer.serializeTrackList(importerResult.importSensorFixData));
@@ -12,11 +12,17 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.commons.fileupload.FileItem;
import org.json.simple.parser.ParseException;
import org.osgi.framework.BundleContext;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
@@ -25,31 +31,45 @@ import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImp
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogDenoteForTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogStartTrackingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.Placemark;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.dto.ExpeditionAllInOneConstants.ImportMode;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.domain.trackimport.DoubleVectorFixImporter;
import com.sap.sailing.domain.trackimport.GPSFixImporter;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.geocoding.ReverseGeocoder;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.trackfiles.impl.ImportResultDTO.ErrorImportDTO;
import com.sap.sailing.server.gateway.trackfiles.impl.ImportResultDTO.TrackImportDTO;
@@ -62,48 +82,91 @@ import com.sap.sailing.server.operationaltransformation.CreateLeaderboardGroup;
import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard;
import com.sap.sailing.server.operationaltransformation.UpdateEvent;
import com.sap.sailing.server.util.WaitForTrackedRaceUtil;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TypeBasedServiceFinderFactory;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.Util.Triple;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.i18n.ResourceBundleStringMessages;
/**
* Importer for expedition data that imports all available data for a boat:
* <ul>
* <li>{@link GPSFixMoving} and {@link BravoExtendedFix} instances are imported as a distinct track that needs to be
* mapped to a specific competitor afterwards</li>
* <li>Wind fixes are being imported as a new {@link WindTrack}</li>
* </ul>
* There are several {@link ImportMode ImportModes} which have specific preconditions and activate different importing
* behavior:
* <ul>
* <li>{@link ImportMode#NEW_EVENT} requires a boatclass name to be given. In this case new {@link Event},
* {@link Regatta} and {@link RegattaLeaderboard} entities are created with one {@link RaceColumn} for a new
* {@link TrackedRace} to be the target of the imported data. The names of the created entities as well as the created
* {@link RaceColumn} and {@link WindSource} are generated from the name of the given {@link FileItem}.</li>
* <li>{@link ImportMode#NEW_COMPETITOR} requires a regatta name to be given. In this case the wind data is being
* imported as new {@link WindSource} to any existing race of the given regatta. The name of the {@link WindSource} is
* determined from the name of the given {@link FileItem}. No new entities are created.</li>
* <li>{@link ImportMode#NEW_RACE} requires a regatta name to be given. In this case the a new {@link RaceColumn} is
* created in the last existing {@link Series}. A new {@link WindSource} is added to the {@link TrackedRace} associated
* with the newly added {@link RaceColumn}. This mode does not support importing in cases where fleet racing is used by
* a {@link Regatta}.</li>
* </ul>
* The imported {@link GPSFixMoving} and {@link BravoExtendedFix} tracks aren't mapped to a {@link Competitor} by the
* importer. Instead the IDs of the imported tracks are contained in the result and are expected to be mapped by the
* user afterwards.
*
* This importer is intended to be used by {@link ExpeditionAllInOneImportServlet}.
*/
public class ExpeditionAllInOneImporter {
private static final Logger logger = Logger.getLogger(ExpeditionAllInOneImporter.class.getName());
private static final String ERROR_MESSAGE_GPS_DATA_IMPORT_FAILED = "Failed to import GPS data!";
private static final String ERROR_MESSAGE_SENSOR_DATA_IMPORT_FAILED = "Failed to import sensor data!";
private static final String ERROR_MESSAGE_BOAT_CLASS_DETERMINATION_FAILED = "Failed to determine boat class!";
private static final double VENUE_RANGE_CHECK = 10;
private final RacingEventService service;
private final RaceLogTrackingAdapter adapter;
private final TypeBasedServiceFinderFactory serviceFinderFactory;
private final BundleContext context;
private ResourceBundleStringMessages serverStringMessages;
private Locale uiLocale;
public static class ImporterResult {
final UUID eventId;
final String leaderboardName, leaderboardGroupName, regattaName, raceName, raceColumnName, fleetName;
final List<Triple<String, String, String>> raceNameRaceColumnNameFleetnameList = new ArrayList<>();
final String leaderboardName, leaderboardGroupName, regattaName;
final List<TrackImportDTO> importGpsFixData, importSensorFixData;
final String sensorFixImporterType;
final List<ErrorImportDTO> errorList = new ArrayList<>();
public ImporterResult(String error) {
this(null, "", "", "", Collections.emptyList(), Collections.emptyList(),
Collections.emptyList(), "", Collections.emptyList());
errorList.add(new ErrorImportDTO(error));
}
public ImporterResult(Throwable exception, List<ErrorImportDTO> additionalErrors) {
this(null, "", "", new RegattaNameAndRaceName("", ""), "", "", Collections.emptyList(),
Collections.emptyList(), "", additionalErrors);
this.errorList.add(new ErrorImportDTO(exception.getClass().getName(), exception.getMessage()));
this(null, "", "","", Collections.emptyList(), Collections.emptyList(),
Collections.emptyList(), "", Collections.emptyList());
errorList.add(new ErrorImportDTO(exception.getClass().getName(), exception.getMessage()));
if (additionalErrors != null) {
errorList.addAll(additionalErrors);
}
}
private ImporterResult(final UUID eventId, final String leaderboardName, String leaderboardGroupName,
final RegattaAndRaceIdentifier regattaAndRaceIdentifier, final String raceColumnName,
final String fleetName, final List<TrackImportDTO> importGpsFixData,
final List<TrackImportDTO> importSensorFixData, final String sensorFixImporterType,
List<ErrorImportDTO> errors) {
final String regattaName, List<Triple<String, String, String>> raceNameRaceColumnNameFleetnameList,
final List<TrackImportDTO> importGpsFixData, final List<TrackImportDTO> importSensorFixData,
final String sensorFixImporterType, List<ErrorImportDTO> errors) {
this.eventId = eventId;
this.leaderboardName = leaderboardName;
this.leaderboardGroupName = leaderboardGroupName;
this.regattaName = regattaAndRaceIdentifier.getRegattaName();
this.raceName = regattaAndRaceIdentifier.getRaceName();
this.raceColumnName = raceColumnName;
this.fleetName = fleetName;
this.regattaName = regattaName;
if (raceNameRaceColumnNameFleetnameList != null) {
this.raceNameRaceColumnNameFleetnameList.addAll(raceNameRaceColumnNameFleetnameList);
}
this.importGpsFixData = importGpsFixData;
this.importSensorFixData = importSensorFixData;
this.sensorFixImporterType = sensorFixImporterType;
@@ -111,8 +174,10 @@ public class ExpeditionAllInOneImporter {
}
}
public ExpeditionAllInOneImporter(final RacingEventService service, RaceLogTrackingAdapter adapter,
public ExpeditionAllInOneImporter(ResourceBundleStringMessages serverStringMessages, Locale uiLocale, final RacingEventService service, RaceLogTrackingAdapter adapter,
final TypeBasedServiceFinderFactory serviceFinderFactory, final BundleContext context) {
this.serverStringMessages = serverStringMessages;
this.uiLocale = uiLocale;
this.service = service;
this.adapter = adapter;
this.serviceFinderFactory = serviceFinderFactory;
@@ -120,36 +185,16 @@ public class ExpeditionAllInOneImporter {
}
public ImporterResult importFiles(final String filenameWithSuffix, final FileItem fileItem,
final String boatClassName) throws AllinOneImportException {
final String boatClassName, ImportMode importMode, String existingRegattaName) throws AllinOneImportException {
final List<ErrorImportDTO> errors = new ArrayList<>();
final String importTimeString = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now(ZoneOffset.UTC));
final String filename = ExpeditionImportFilenameUtils.truncateFilenameExtentions(filenameWithSuffix);
final String filenameWithDateTimeSuffix = filename + "_" + importTimeString;
final String eventName = filenameWithDateTimeSuffix;
final String description = MessageFormat.format("Event imported from expedition file ''{0}'' on {1}",
filenameWithSuffix, importTimeString);
final String leaderboardGroupName = filenameWithDateTimeSuffix;
final String regattaNameAndleaderboardName = filenameWithDateTimeSuffix;
final RegattaIdentifier regattaIdentifier = new RegattaName(filenameWithDateTimeSuffix);
final String raceColumnName = filename;
final String trackedRaceName = filenameWithDateTimeSuffix;
final String courseAreaName = "Default";
final UUID courseAreaId = UUID.randomUUID();
// This is just the default used in the UI
final Double buoyZoneRadiusInHullLengths = 3.0;
// TODO is this a proper id for the WindSource used here?
final String windSourceId = filenameWithDateTimeSuffix;
// TODO guess venue based on the reverse geocoder?
final String venueName = filename;
final String fleetName = LeaderboardNameConstants.DEFAULT_FLEET_NAME;
final String seriesName = Series.DEFAULT_NAME;
// TODO wild guess...
final ScoringSchemeType scoringSchemeType = ScoringSchemeType.LOW_POINT;
final RankingMetrics rankingMetric = RankingMetrics.ONE_DESIGN;
final int[] discardThresholds = new int[0];
final ImportResultDTO jsonHolderForGpsFixImport = new ImportResultDTO(logger);
@@ -157,12 +202,14 @@ public class ExpeditionAllInOneImporter {
try {
new TrackFilesImporter(service, serviceFinderFactory, context).importFixes(jsonHolderForGpsFixImport,
GPSFixImporter.EXPEDITION_TYPE, filesForGpsFixImport);
this.ensureSuccessfulImport(jsonHolderForGpsFixImport, ERROR_MESSAGE_GPS_DATA_IMPORT_FAILED);
this.ensureSuccessfulImport(jsonHolderForGpsFixImport,
serverStringMessages.get(uiLocale, "allInOneErrorGPSDataImportFailed"));
} catch (IOException e1) {
errors.addAll(jsonHolderForGpsFixImport.getErrorList());
throw new AllinOneImportException(e1, errors);
}
errors.addAll(jsonHolderForGpsFixImport.getErrorList());
final ImportResultDTO jsonHolderForSensorFixImport = new ImportResultDTO(logger);
final String sensorFixImporterType = DoubleVectorFixImporter.EXPEDITION_EXTENDED_TYPE;
@@ -171,7 +218,8 @@ public class ExpeditionAllInOneImporter {
try {
new SensorDataImporter(service, context).importFiles(false, jsonHolderForSensorFixImport,
importerNamesAndFilesForSensorFixImport);
this.ensureSuccessfulImport(jsonHolderForSensorFixImport, ERROR_MESSAGE_SENSOR_DATA_IMPORT_FAILED);
this.ensureSuccessfulImport(jsonHolderForSensorFixImport,
serverStringMessages.get(uiLocale, "allInOneErrorSensorDataImportFailed"));
} catch (IOException e1) {
errors.addAll(jsonHolderForSensorFixImport.getErrorList());
throw new AllinOneImportException(e1, errors);
@@ -194,14 +242,244 @@ public class ExpeditionAllInOneImporter {
lastFixAt = deviceTrackEnd;
}
}
final TimePoint eventStartDate = firstFixAt;
final TimePoint eventEndDate = lastFixAt;
final UUID eventId;
final String leaderboardGroupName;
final String regattaNameAndleaderboardName;
final List<Triple<String, String, String>> raceNameRaceColumnNameFleetnameList = new ArrayList<>();
final List<DynamicTrackedRace> trackedRaces = new ArrayList<>();
if (importMode == ImportMode.NEW_EVENT) {
leaderboardGroupName = filenameWithDateTimeSuffix;
regattaNameAndleaderboardName = filenameWithDateTimeSuffix;
String raceColumnName = filename;
eventId = UUID.randomUUID();
String fleetName = LeaderboardNameConstants.DEFAULT_FLEET_NAME;
final DynamicTrackedRace trackedRace = createEventStructureWithASingleRaceAndTrackIt(filenameWithSuffix, boatClassName, errors, importTimeString, filename,
filenameWithDateTimeSuffix, trackedRaceName, discardThresholds, firstFixAt, lastFixAt,
eventStartDate, eventEndDate, eventId, leaderboardGroupName, regattaNameAndleaderboardName,
fleetName, raceColumnName);
trackedRaces.add(trackedRace);
raceNameRaceColumnNameFleetnameList.add(new Triple<>(trackedRaceName, raceColumnName, fleetName));
updateVenueName(filename, jsonHolderForGpsFixImport.getImportResult(), eventId);
} else {
regattaNameAndleaderboardName = existingRegattaName;
if (existingRegattaName != null && !existingRegattaName.isEmpty()) {
final Regatta regatta = service.getRegattaByName(existingRegattaName);
if (regatta == null) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidData"));
}
final Leaderboard leaderboard = service.getLeaderboardByName(existingRegattaName);
if (leaderboard == null || !(leaderboard instanceof RegattaLeaderboard)) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidLeaderBoard"));
}
final RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard;
final Pair<Event, LeaderboardGroup> foundEventAndLeaderboardGroup = findEventAndLeaderboardGroupForExistingLeaderboard(leaderboard);
if (foundEventAndLeaderboardGroup == null) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidLeaderBoardEventLink"));
}
eventId = foundEventAndLeaderboardGroup.getA().getId();
ensureEventLongEnough(firstFixAt, lastFixAt, eventId);
leaderboardGroupName = foundEventAndLeaderboardGroup.getB().getName();
if (importMode == ImportMode.NEW_COMPETITOR) {
final Iterable<RaceColumn> raceColumns = regattaLeaderboard.getRaceColumns();
if (Util.isEmpty(raceColumns)) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidRace"));
}
try {
for (RaceColumn raceColumn : raceColumns) {
final Iterable<? extends Fleet> fleets = raceColumn.getFleets();
if (Util.size(fleets) != 1) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorSplitFleetNotSupported"));
}
final Fleet fleet = fleets.iterator().next();
DynamicTrackedRace trackedRaceForColumn = (DynamicTrackedRace) raceColumn.getTrackedRace(fleet);
if (trackedRaceForColumn == null) {
trackedRaceForColumn = trackRace(regattaLeaderboard, raceColumn, fleet);
}
trackedRaces.add(trackedRaceForColumn);
raceNameRaceColumnNameFleetnameList
.add(new Triple<>(trackedRaceForColumn.getRaceIdentifier().getRaceName(),
raceColumn.getName(), fleet.getName()));
}
} catch (Exception e) {
throw new AllinOneImportException(e, errors);
}
} else if (importMode == ImportMode.NEW_RACE) {
final Iterable<? extends Series> seriesInRegatta = regatta.getSeries();
if (Util.isEmpty(seriesInRegatta)) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidSeries"));
}
final Series series = Util.get(seriesInRegatta, Util.size(seriesInRegatta) - 1);
final Iterable<? extends Fleet> fleets = series.getFleets();
if (Util.size(fleets) != 1) {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorMultiSeries"));
}
final Fleet fleet = fleets.iterator().next();
final String fleetName = fleet.getName();
// When uploading files with identical name, the second RaceColumn will be named with the upload time in its name
final String raceColumnName = regatta.getRaceColumnByName(filename) == null ? filename : filenameWithDateTimeSuffix;
final RaceColumn raceColumn = service.apply(new AddColumnToSeries(regatta.getRegattaIdentifier(), series.getName(), raceColumnName));
final Event event = service.addEvent(eventName, description, eventStartDate, eventEndDate, venueName, true,
UUID.randomUUID());
final DynamicTrackedRace trackedRace = createTrackedRaceAndSetupRaceTimes(errors, trackedRaceName, firstFixAt, lastFixAt, regatta, regattaLeaderboard,
raceColumn, fleet);
trackedRaces.add(trackedRace);
raceNameRaceColumnNameFleetnameList.add(new Triple<>(trackedRaceName, raceColumnName, fleetName));
} else {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidImportMode") + importMode);
}
} else {
return new ImporterResult(serverStringMessages.get(uiLocale, "allInOneErrorInvalidRegattaName"));
}
}
try {
final WindImportResult windImportResult = new AbstractWindImporter.WindImportResult();
final WindSourceWithAdditionalID windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION,
windSourceId);
final Map<InputStream, String> streamsWithFilenames = new HashMap<>();
streamsWithFilenames.put(fileItem.getInputStream(), filenameWithSuffix);
new WindImporter().importWindToWindSourceAndTrackedRaces(service, windImportResult, windSource,
trackedRaces, streamsWithFilenames);
return new ImporterResult(eventId, regattaNameAndleaderboardName, leaderboardGroupName,
regattaNameAndleaderboardName, raceNameRaceColumnNameFleetnameList,
jsonHolderForGpsFixImport.getImportResult(), jsonHolderForSensorFixImport.getImportResult(),
sensorFixImporterType, errors);
} catch (Exception e) {
throw new AllinOneImportException(e, errors);
}
}
private void ensureEventLongEnough(TimePoint firstFixAt, TimePoint lastFixAt, UUID eventId) {
Event event = service.getEvent(eventId);
TimePoint startDate = event.getStartDate();
if(firstFixAt.before(startDate)) {
startDate = firstFixAt;
}
TimePoint endDate = event.getEndDate();
if(lastFixAt.after(endDate)) {
endDate = lastFixAt;
}
Iterable<UUID> leaderboardGroups = StreamSupport.stream(event.getLeaderboardGroups().spliterator(), false).map(t -> t.getId()).collect(Collectors.toList());
service.apply(new UpdateEvent(event.getId(), event.getName(), event.getDescription(), startDate,
endDate, event.getVenue().getName(), event.isPublic(),
leaderboardGroups, event.getOfficialWebsiteURL(), event.getBaseURL(),
event.getSailorsInfoWebsiteURLs(), event.getImages(), event.getVideos(), event.getWindFinderReviewedSpotsCollectionIds()));
}
private Pair<Event, LeaderboardGroup> findEventAndLeaderboardGroupForExistingLeaderboard(final Leaderboard leaderboard) {
for (Event event : service.getAllEvents()) {
for (LeaderboardGroup leaderboardGroup : event.getLeaderboardGroups()) {
for (Leaderboard lb : leaderboardGroup.getLeaderboards()) {
if (lb.equals(leaderboard)) {
return new Pair<>(event, leaderboardGroup);
}
}
}
}
return null;
}
private DynamicTrackedRace createEventStructureWithASingleRaceAndTrackIt(final String filenameWithSuffix, final String boatClassName,
final List<ErrorImportDTO> errors, final String importTimeString, final String filename,
final String filenameWithDateTimeSuffix, final String trackedRaceName, final int[] discardThresholds,
TimePoint firstFixAt, TimePoint lastFixAt, final TimePoint eventStartDate, final TimePoint eventEndDate,
final UUID eventId, final String leaderboardGroupName, final String regattaNameAndleaderboardName,
final String fleetName, final String raceColumnName) throws AllinOneImportException {
final DynamicTrackedRace trackedRace;
final String eventName = filenameWithDateTimeSuffix;
final String description = MessageFormat.format("Event imported from expedition file ''{0}'' on {1}",
filenameWithSuffix, importTimeString);
final RegattaIdentifier regattaIdentifier = new RegattaName(filenameWithDateTimeSuffix);
// This is just the default used in the UI
final Double buoyZoneRadiusInHullLengths = 3.0;
final String seriesName = Series.DEFAULT_NAME;
final Event event = service.addEvent(eventName, description, eventStartDate, eventEndDate, filename, true,
eventId);
final UUID courseAreaId = addDefaultCourseArea(event);
final Regatta regatta = createRegattaWithOneRaceColumn(boatClassName, regattaNameAndleaderboardName,
fleetName, raceColumnName, regattaIdentifier, courseAreaId, buoyZoneRadiusInHullLengths,
seriesName);
final RegattaLeaderboard regattaLeaderboard = service.apply(new CreateRegattaLeaderboard(regattaIdentifier, null,
discardThresholds));
createLeaderboardGroupAndAddItToTheEvent(leaderboardGroupName, regattaNameAndleaderboardName, description, event);
final RaceColumn raceColumn = regattaLeaderboard.getRaceColumns().iterator().next();
final Fleet fleet = raceColumn.getFleets().iterator().next();
trackedRace = createTrackedRaceAndSetupRaceTimes(errors, trackedRaceName, firstFixAt, lastFixAt, regatta, regattaLeaderboard,
raceColumn, fleet);
return trackedRace;
}
private void updateVenueName(String filename, List<TrackImportDTO> list, UUID eventId) {
for (TrackImportDTO f : list) {
TrackFileImportDeviceIdentifier deviceIdentifier = TrackFileImportDeviceIdentifierImpl
.getOrCreate(f.getDevice());
final TimePoint end = f.getRange().to();
try {
final CompletableFuture<GPSFix> waitForFix = new CompletableFuture<>();
service.getSensorFixStore().loadFixes(waitForFix::complete, deviceIdentifier, end, end, true);
GPSFix gpsFix = waitForFix.getNow(null);
if (gpsFix != null) {
Position pos = gpsFix.getPosition();
if (pos != null) {
try {
Placemark reverseVenue = ReverseGeocoder.INSTANCE.getPlacemarkFirst(pos, VENUE_RANGE_CHECK,
new Placemark.ByPopulationDistanceRatio(pos));
if (reverseVenue != null) {
String newVenueName = reverseVenue.getName();
Event event = service.getEvent(eventId);
Iterable<UUID> leaderboardGroups = StreamSupport
.stream(event.getLeaderboardGroups().spliterator(), false).map(t -> t.getId())
.collect(Collectors.toList());
service.apply(new UpdateEvent(event.getId(), event.getName(), event.getDescription(),
event.getStartDate(), event.getEndDate(), newVenueName, event.isPublic(),
leaderboardGroups, event.getOfficialWebsiteURL(), event.getBaseURL(),
event.getSailorsInfoWebsiteURLs(), event.getImages(), event.getVideos(),
event.getWindFinderReviewedSpotsCollectionIds()));
break;
}
} catch (IOException | ParseException e) {
logger.log(Level.WARNING, "Could not reverse determine location " + pos, e);
}
}
}
} catch (NoCorrespondingServiceRegisteredException | TransformationException e) {
logger.log(Level.WARNING, "Could not reverse determine location", e);
}
}
}
private UUID addDefaultCourseArea(final Event event) {
final String courseAreaName = "Default";
final UUID courseAreaId = UUID.randomUUID();
service.addCourseAreas(event.getId(), new String[] { courseAreaName }, new UUID[] { courseAreaId });
return courseAreaId;
}
private Regatta createRegattaWithOneRaceColumn(final String boatClassName, final String regattaNameAndleaderboardName, final String fleetName,
final String raceColumnName, final RegattaIdentifier regattaIdentifier, final UUID courseAreaId,
final Double buoyZoneRadiusInHullLengths, final String seriesName) throws AllinOneImportException {
// TODO wild guess...
final ScoringSchemeType scoringSchemeType = ScoringSchemeType.LOW_POINT;
final RankingMetrics rankingMetric = RankingMetrics.ONE_DESIGN;
final Regatta regatta;
final ScoringScheme scoringScheme = service.getBaseDomainFactory().createScoringScheme(scoringSchemeType);
final LinkedHashMap<String, SeriesCreationParametersDTO> seriesCreationParameters = new LinkedHashMap<>();
@@ -214,73 +492,70 @@ public class ExpeditionAllInOneImporter {
final RegattaCreationParametersDTO regattaCreationParameters = new RegattaCreationParametersDTO(
seriesCreationParameters);
final Regatta regatta = service.apply(new AddSpecificRegatta(regattaNameAndleaderboardName, boatClassName,
/* can boats of competitors change */ false,
/* start date */ null, /* end date */ null, UUID.randomUUID(),
regatta = service.apply(new AddSpecificRegatta(regattaNameAndleaderboardName, boatClassName,
/* can boats of competitors change */ false,
/* start date */ null, /* end date */ null, UUID.randomUUID(),
regattaCreationParameters, true, scoringScheme, courseAreaId, buoyZoneRadiusInHullLengths, true,
false, rankingMetric));
this.ensureBoatClassDetermination(regatta);
service.apply(new AddColumnToSeries(regattaIdentifier, seriesName, raceColumnName));
final RegattaLeaderboard regattaLeaderboard = service.apply(new CreateRegattaLeaderboard(regattaIdentifier, null,
discardThresholds));
return regatta;
}
private void createLeaderboardGroupAndAddItToTheEvent(final String leaderboardGroupName, final String regattaNameAndleaderboardName,
final String description, final Event event) {
final LeaderboardGroup leaderboardGroup = service.apply(new CreateLeaderboardGroup(leaderboardGroupName,
description, null, false, Collections.singletonList(regattaNameAndleaderboardName), null, null));
service.apply(new UpdateEvent(event.getId(), event.getName(), event.getDescription(), event.getStartDate(),
event.getEndDate(), event.getVenue().getName(), event.isPublic(),
Collections.singleton(leaderboardGroup.getId()), event.getOfficialWebsiteURL(), event.getBaseURL(),
event.getSailorsInfoWebsiteURLs(), event.getImages(), event.getVideos(), event.getWindFinderReviewedSpotsCollectionIds()));
}
final RaceColumn raceColumn = regattaLeaderboard.getRaceColumns().iterator().next();
final Fleet fleet = raceColumn.getFleets().iterator().next();
private DynamicTrackedRace createTrackedRaceAndSetupRaceTimes(final List<ErrorImportDTO> errors,
final String trackedRaceName, TimePoint firstFixAt, TimePoint lastFixAt, final Regatta regatta,
final RegattaLeaderboard regattaLeaderboard, final RaceColumn raceColumn, final Fleet fleet)
throws AllinOneImportException {
final RaceLog raceLog = raceColumn.getRaceLog(fleet);
final AbstractLogEventAuthor author = service.getServerAuthor();
final TimePoint startOfTracking = firstFixAt;
final TimePoint endOfTracking = lastFixAt;
raceLog.add(new RaceLogStartOfTrackingEventImpl(startOfTracking, author, raceLog.getCurrentPassId()));
raceLog.add(new RaceLogEndOfTrackingEventImpl(endOfTracking, author, raceLog.getCurrentPassId()));
// TODO explicitly set startOfRace?
try {
TimePoint startTrackingTimePoint = MillisecondsTimePoint.now();
// this ensures that the events consistently have different timepoints to ensure a consistent result of the state analysis
// that's why we can't jus call adapter.denoteRaceForRaceLogTracking
// this ensures that the events consistently have different timepoints to ensure a consistent result of the
// state analysis
// that's why we can't just call adapter.denoteRaceForRaceLogTracking
final TimePoint denotationTimePoint = startTrackingTimePoint.minus(1);
raceLog.add(new RaceLogDenoteForTrackingEventImpl(denotationTimePoint,
service.getServerAuthor(), raceLog.getCurrentPassId(), trackedRaceName, regatta.getBoatClass(), UUID.randomUUID()));
raceLog.add(new RaceLogStartTrackingEventImpl(startTrackingTimePoint, author, raceLog.getCurrentPassId()));
final RaceHandle raceHandle = adapter.startTracking(service, regattaLeaderboard, raceColumn, fleet,
/* trackWind */ true, /* correctWindDirectionByMagneticDeclination */ true);
// wait for the RaceDefinition to be created
raceHandle.getRace();
final DynamicTrackedRace trackedRace = WaitForTrackedRaceUtil.waitForTrackedRace(raceColumn, fleet, 10);
if (trackedRace == null) {
throw new IllegalStateException("Could not obtain imported race");
}
final WindImportResult windImportResult = new AbstractWindImporter.WindImportResult();
final WindSourceWithAdditionalID windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION,
windSourceId);
final Map<InputStream, String> streamsWithFilenames = new HashMap<>();
streamsWithFilenames.put(fileItem.getInputStream(), filenameWithSuffix);
new WindImporter().importWindToWindSourceAndTrackedRaces(service, windImportResult, windSource,
Arrays.asList(trackedRace), streamsWithFilenames);
raceLog.add(new RaceLogDenoteForTrackingEventImpl(denotationTimePoint, service.getServerAuthor(),
raceLog.getCurrentPassId(), trackedRaceName, regatta.getBoatClass(), UUID.randomUUID()));
return new ImporterResult(event.getId(), regattaNameAndleaderboardName, leaderboardGroupName,
trackedRace.getRaceIdentifier(), raceColumnName, fleetName,
jsonHolderForGpsFixImport.getImportResult(), jsonHolderForSensorFixImport.getImportResult(),
sensorFixImporterType, errors);
raceLog.add(new RaceLogStartTrackingEventImpl(startTrackingTimePoint, author, raceLog.getCurrentPassId()));
return trackRace(regattaLeaderboard, raceColumn, fleet);
} catch (Exception e) {
throw new AllinOneImportException(e, errors);
}
}
private DynamicTrackedRace trackRace(final RegattaLeaderboard regattaLeaderboard, final RaceColumn raceColumn,
final Fleet fleet) throws NotDenotedForRaceLogTrackingException, Exception {
DynamicTrackedRace trackedRace;
final RaceHandle raceHandle = adapter.startTracking(service, regattaLeaderboard, raceColumn, fleet,
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true);
// wait for the RaceDefinition to be created
raceHandle.getRace();
trackedRace = WaitForTrackedRaceUtil.waitForTrackedRace(raceColumn, fleet, 10);
if (trackedRace == null) {
throw new IllegalStateException("Could not obtain imported race");
}
return trackedRace;
}
private void ensureSuccessfulImport(ImportResultDTO result, String errorMessage) throws AllinOneImportException {
if (!result.getErrorList().isEmpty()) {
throw new AllinOneImportException(errorMessage, result.getErrorList());
@@ -289,7 +564,7 @@ public class ExpeditionAllInOneImporter {
private void ensureBoatClassDetermination(Regatta regatta) throws AllinOneImportException {
if (regatta.getBoatClass() == null) {
throw new AllinOneImportException(ERROR_MESSAGE_BOAT_CLASS_DETERMINATION_FAILED);
throw new AllinOneImportException(serverStringMessages.get(uiLocale, "allInOneErrorBoatClassDeterminationFailed"));
}
}
}
@@ -50,8 +50,12 @@ class ImportResultDTO {
private final String filename;
private final String requestedImporter;
ErrorImportDTO(String name, String message) {
this(null, name, message, null, null);
ErrorImportDTO(String message) {
this(null, null, message, null, null);
}
ErrorImportDTO(String classname, String message) {
this(null, classname, message, null, null);
}
ErrorImportDTO(String exUUID, String name, String message, String filename, String requestedImporter) {
@@ -8,6 +8,7 @@ import org.json.simple.JSONObject;
import com.sap.sailing.server.gateway.trackfiles.impl.ImportResultDTO.ErrorImportDTO;
import com.sap.sailing.server.gateway.trackfiles.impl.ImportResultDTO.TrackImportDTO;
import com.sap.sse.common.Util.Triple;
/**
* Utility class providing convenience methods to serialize import result objects into JSON objects.
@@ -34,6 +35,18 @@ class ImportResultSerializer {
static JSONArray serializeErrorList(List<ErrorImportDTO> errorList) {
return serializeList(errorList, ImportResultSerializer::serializeError);
}
public static JSONArray serializeRaceList(List<Triple<String, String, String>> raceNameRaceColumnNameFleetnameList) {
return serializeList(raceNameRaceColumnNameFleetnameList, ImportResultSerializer::serializeRaceEntry);
}
private static JSONObject serializeRaceEntry(Triple<String, String, String> entry) {
final JSONObject json = new JSONObject();
json.put("raceName", entry.getA());
json.put("raceColumnName", entry.getB());
json.put("fleetName", entry.getC());
return json;
}
private static JSONObject serializeError(ErrorImportDTO error) {
final JSONObject json = new JSONObject();
@@ -44,4 +57,6 @@ class ImportResultSerializer {
json.put("message", error.getMessage());
return json;
}
}
@@ -22,7 +22,7 @@
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=cp1252 -Dexpedition.udp.port=5010 -Xmx6000m -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Dpolardata.source.url=https://www.sapsailing.com -Drestore.tracked.races=true"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=cp1252 -Dexpedition.udp.port=5010 -Xmx6000m -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Dpolardata.source.url=https://www.sapsailing.com -Drestore.tracked.races=false"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
<stringAttribute key="pde.version" value="3.3"/>
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_TENANT_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%"/>
@@ -31,6 +31,8 @@
Any previously imported expedition tracks may be dropped and reimported to gain access to the newly introduced measures.</li>
<li>The settings dialog of the competitor charts in <tt>RaceBoard.html</tt> now provides the measures in alphabetical order (based on the current locale).
Expedition specific measures - if available - are shown at the end of the list.</li>
<li>The all in one expedition import has been extended to be able to import new competitor tracks or new races to an existing regatta.
Previously it was only possible to import a new event structure.</li>
</ul>
<h2 class="articleSubheadline">March 2018</h2>
@@ -493,6 +493,15 @@ public class Util {
return joinStrings(separator, Arrays.asList(strings));
}
public static String join(String separator, Object... objects) {
final String[] strings = new String[objects.length];
int i=0;
for (Object o : objects) {
strings[i++] = o.toString();
}
return joinStrings(separator, Arrays.asList(strings));
}
public static String joinStrings(String separator, Iterable<String> strings) {
StringBuilder result = new StringBuilder();
boolean first = true;
@@ -63,6 +63,11 @@ public abstract class AbstractEntryPoint<S extends StringMessages> implements En
public void reportError(String message) {
errorReporter.reportError(message);
}
@Override
public void reportError(String title, String message) {
errorReporter.reportError(title, message);
}
@Override
public void reportError(String message, boolean silentMode) {
@@ -2,6 +2,7 @@ package com.sap.sse.gwt.client;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
@@ -26,6 +27,16 @@ public class DefaultErrorReporter<S extends StringMessages> implements ErrorRepo
persistentAlertLabel.setStyleName("global-alert-message");
}
@Override
public void reportError(String title, String message) {
errorDialogBox.setText(title);
serverResponseLabel.addStyleName("serverResponseLabelError"); //$NON-NLS-1$
serverResponseLabel.setHTML(SafeHtmlUtils.fromString(message).asString() + "<br><br>");
errorDialogBox.center();
dialogCloseButton.setFocus(true);
}
@Override
public void reportError(String message) {
errorDialogBox.setText(message);
@@ -2,7 +2,11 @@ package com.sap.sse.gwt.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.rpc.RpcRequestBuilder;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.sap.sse.gwt.shared.RpcConstants;
public class EntryPointHelper {
/**
@@ -18,6 +22,13 @@ public class EntryPointHelper {
String moduleBaseURL = GWT.getModuleBaseURL();
String baseURL = moduleBaseURL.substring(0, moduleBaseURL.lastIndexOf('/', moduleBaseURL.length()-2)+1);
serviceToRegister.setServiceEntryPoint(baseURL + servicePath);
serviceToRegister.setRpcRequestBuilder(new RpcRequestBuilder() {
@Override
protected void doFinish(RequestBuilder rb) {
super.doFinish(rb);
rb.setHeader(RpcConstants.HEADER_LOCALE, LocaleInfo.getCurrentLocale().getLocaleName());
}
});
}
/**
@@ -4,8 +4,11 @@ import com.google.gwt.user.client.ui.Widget;
public interface ErrorReporter {
void reportError(String message);
void reportError(String title, String message);
void reportError(String message, boolean silentMode);
void reportPersistentInformation(String message);
Widget getPersistentInformationWidget();
}
@@ -1,6 +1,7 @@
package com.sap.sse.gwt.dispatch.servlets;
import java.io.IOException;
import java.util.Locale;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
@@ -10,6 +11,7 @@ import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.sap.sse.gwt.shared.RpcConstants;
/**
* Using GWT in a proxyfied environment can be tricky and leads to strange errors
@@ -66,5 +68,21 @@ public abstract class ProxiedRemoteServiceServlet extends RemoteServiceServlet {
} else {
super.doUnexpectedFailure(e);
}
}
}
protected Locale getClientLocale() {
final HttpServletRequest request = getThreadLocalRequest();
if (request != null) {
final String localeString = request.getHeader(RpcConstants.HEADER_LOCALE);
if (localeString != null && ! localeString.isEmpty()) {
try {
return Locale.forLanguageTag(localeString);
} catch (Exception e) {
// non-parseable locales are ignored
}
}
}
// Using default locale if the client locale could not be determined
return Locale.ENGLISH;
}
}
@@ -0,0 +1,5 @@
package com.sap.sse.gwt.shared;
public interface RpcConstants {
public static final String HEADER_LOCALE = "X-Client-Locale";
}
@@ -0,0 +1,23 @@
package com.sap.sse.util.impl;
import java.io.Serializable;
import java.util.UUID;
import java.util.logging.Logger;
public class UUIDHelper {
private static final Logger logger = Logger.getLogger(UUIDHelper.class.getName());
/**
* If the {@link Object#toString() string representation} of {@code serializableId} {@link UUID#fromString(String) parses}
* as a {@link UUID}, the resulting {@link UUID} is returned. Otherwise, the {@code serializableId} parameter's value
* is returned unchanged.
*/
public static Serializable tryUuidConversion(Serializable serializableId) {
try {
return UUID.fromString(serializableId.toString());
} catch (IllegalArgumentException iae) {
logger.fine("The serializable " + serializableId.toString() + " could not be converted to a UUID");
}
return serializableId;
}
}