mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-04 01:59:35 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
523d85d61e
67 files changed
+1407
-342
No files matched your search
@@ -232,6 +232,7 @@ if [[ "$@" == "release" ]]; then
|
||||
fi
|
||||
|
||||
mkdir -p $PROJECT_HOME/dist
|
||||
rm -rf $PROJECT_HOME/dist/*
|
||||
mkdir -p $PROJECT_HOME/build
|
||||
|
||||
RELEASE_NOTES="Release $VERSION_INFO\n$RELEASE_NOTES"
|
||||
|
||||
Regular → Executable
File mode changed.
+7
@@ -1,7 +1,9 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
|
||||
public abstract class AbstractSpeedImpl implements Speed {
|
||||
|
||||
@@ -17,6 +19,11 @@ public abstract class AbstractSpeedImpl implements Speed {
|
||||
return new NauticalMileDistance((t2.asMillis() - t1.asMillis()) / 1000. / 3600. * getKnots());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDuration(Distance distance) {
|
||||
return new MillisecondsDurationImpl((long) (1000 * distance.getMeters() / getMetersPerSecond()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMetersPerSecond() {
|
||||
return getKnots() * Mile.METERS_PER_SEA_MILE / 3600;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.shared.Unit;
|
||||
import com.sap.sse.datamining.shared.annotations.Statistic;
|
||||
@@ -46,6 +47,11 @@ public interface Speed extends Comparable<Speed>, Serializable {
|
||||
return Distance.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDuration(Distance distance) {
|
||||
throw new ArithmeticException("Cannot determine duration for any distance with zero speed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "0kn";
|
||||
@@ -68,4 +74,10 @@ public interface Speed extends Comparable<Speed>, Serializable {
|
||||
*/
|
||||
Distance travel(TimePoint from, TimePoint to);
|
||||
|
||||
/**
|
||||
* The duration it takes to travel the <code>distance</code> specified on a great circle (the "straight
|
||||
* line" on a sphere) with this speed.
|
||||
*/
|
||||
Duration getDuration(Distance distance);
|
||||
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ package com.sap.sailing.domain.common.racelog;
|
||||
*/
|
||||
public interface RaceLogServletConstants {
|
||||
static final String PARAMS_LEADERBOARD_NAME = "leaderboard";
|
||||
static final String PARAMS_RACE_COLUMN_NAME = "raceColumn";
|
||||
static final String PARAMS_RACE_COLUMN_NAME = "race_column";
|
||||
static final String PARAMS_RACE_FLEET_NAME = "fleet";
|
||||
static final String PARAMS_CLIENT_UUID = "clientuuid";
|
||||
static final String PARAM_COURSE_AREA_FILTER = "courseArea";
|
||||
|
||||
+8
-2
@@ -21,7 +21,8 @@ public class DeviceMappingConstants {
|
||||
// schema and host are case insensitive.
|
||||
// But to adhere to best practices, and due to the deficiencies of some web servers and clients,
|
||||
// all URL components should be treated as case insensitive, preferring _underscores_ to CamelCase.
|
||||
public static final String URL_BASE = "/tracking/checkin";
|
||||
public static final String TRACKING_URL_BASE = "/tracking/checkin";
|
||||
public static final String BUOY_TENDER_URL_BASE = "/buoy-tender/checkin";
|
||||
public static final String URL_EVENT_ID = "event_id";
|
||||
public static final String URL_LEADERBOARD_NAME = "leaderboard_name";
|
||||
public static final String URL_COMPETITOR_ID_AS_STRING = "competitor_id";
|
||||
@@ -43,11 +44,16 @@ public class DeviceMappingConstants {
|
||||
|
||||
public static String getDeviceMappingForRegattaLogUrl(String serverUrlWithoutTrailingSlash, String eventId,
|
||||
String leaderboardName, String mappedItemType, String mappedItemId, UrlHelper helper) {
|
||||
return helper.encodeUrl(serverUrlWithoutTrailingSlash + URL_BASE + "?" + URL_EVENT_ID + "=" + eventId + "&"
|
||||
return helper.encodeUrl(serverUrlWithoutTrailingSlash + TRACKING_URL_BASE + "?" + URL_EVENT_ID + "=" + eventId + "&"
|
||||
+ URL_LEADERBOARD_NAME + "=" + leaderboardName + "&" + mappedItemType + "="
|
||||
+ mappedItemId);
|
||||
}
|
||||
|
||||
public static String getBuoyTenderInvitationUrl(String serverUrlWithoutTrailingSlash,
|
||||
String leaderboardName, UrlHelper helper) {
|
||||
return helper.encodeUrl(serverUrlWithoutTrailingSlash + BUOY_TENDER_URL_BASE + "?" + URL_LEADERBOARD_NAME + "=" + leaderboardName);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String getDeviceMappingForRaceLogUrl(String serverUrlWithoutTrailingSlash, String leaderboardName,
|
||||
String raceColumnName, String fleetName, String mappedItemType, String mappedItemId, long fromMillis,
|
||||
|
||||
-1
@@ -120,7 +120,6 @@ public class MongoGPSFixStoreImpl implements MongoGPSFixStore {
|
||||
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark)
|
||||
throws NoCorrespondingServiceRegisteredException, TransformationException{
|
||||
List<DeviceMapping<Mark>> mappings = new DeviceMarkMappingFinder<>(log).analyze().get(mark);
|
||||
|
||||
if (mappings != null) {
|
||||
for (DeviceMapping<Mark> mapping : mappings) {
|
||||
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
|
||||
|
||||
+1
@@ -2,3 +2,4 @@ trackingInvitationFor=Einladung zum Tracken für
|
||||
scanQRCodeOrVisitUrlToRegisterAs=Scanne den QRCode oder klicke den Link mit deinem Smartphone an um dich zu registrieren als
|
||||
welcomeTo=Willkommen zu
|
||||
alternativelyVisitThisLink=Alternativ kannst du diesen Link anklicken
|
||||
buoyTender=Bojenleger
|
||||
+1
@@ -2,3 +2,4 @@ trackingInvitationFor=Tracking Invitation for
|
||||
scanQRCodeOrVisitUrlToRegisterAs=Scan the following QRCode or click the link using your smartphone to register as
|
||||
welcomeTo=Welcome to
|
||||
alternativelyVisitThisLink=Alternatively, you can click this Link
|
||||
buoyTender=buoy tender
|
||||
Regular → Executable
+16
-3
@@ -21,9 +21,9 @@ import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTracki
|
||||
import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.RaceLogRaceTracker;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.RaceHandle;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sse.common.mail.MailException;
|
||||
@@ -40,7 +40,7 @@ public interface RaceLogTrackingAdapter {
|
||||
* <li>Is a {@link RaceLogStartTrackingEvent} present in the racelog? If not, add one</li>
|
||||
* </ul>
|
||||
*/
|
||||
void startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet)
|
||||
RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet)
|
||||
throws NotDenotedForRaceLogTrackingException, Exception;
|
||||
|
||||
RaceLogTrackingState getRaceLogTrackingState(RacingEventService service, RaceColumn raceColumn, Fleet fleet);
|
||||
@@ -76,10 +76,15 @@ public interface RaceLogTrackingAdapter {
|
||||
throws NotDenotableForRaceLogTrackingException;
|
||||
|
||||
/**
|
||||
* Add a fix to the {@link GPSFixStore}, and create a mapping with a virtual device for exactly that timepoint.
|
||||
* @see #pingMark(RaceLog, Mark, GPSFix, RacingEventService) using a random {@link PingDeviceIdentifier}
|
||||
*/
|
||||
void pingMark(RaceLog raceLogToAddTo, Mark mark, GPSFix gpsFix, RacingEventService service);
|
||||
|
||||
/**
|
||||
* @see #pingMark(RaceLog, Mark, GPSFix, RacingEventService)
|
||||
*/
|
||||
void pingMark(RegattaLog regattaLogToAddTo, Mark mark, GPSFix gpsFix, RacingEventService service);
|
||||
|
||||
/**
|
||||
* Duplicate the course and competitor registrations in the newest {@link RaceLogCourseDesignChangedEvent} in
|
||||
* {@code from} race log to the {@code to} race logs. The {@link Mark}s and {@link ControlPoint}s are duplicated and
|
||||
@@ -102,7 +107,15 @@ public interface RaceLogTrackingAdapter {
|
||||
|
||||
/**
|
||||
* Invite competitors for tracking via the Tracking App by sending out emails.
|
||||
* @throws MailException
|
||||
*/
|
||||
void inviteCompetitorsForTrackingViaEmail(Event event, Leaderboard leaderboard,
|
||||
String serverUrlWithoutTrailingSlash, Set<Competitor> competitors, Locale locale) throws MailException;
|
||||
|
||||
/**
|
||||
* Invite buoy tenders for buoy pinging via the Buoy Tender App by sending out emails.
|
||||
* @throws MailException
|
||||
*/
|
||||
void inviteBuoyTenderViaEmail(Event event, Leaderboard leaderboard, String serverUrlWithoutTrailingSlash,
|
||||
String emails, Locale locale) throws MailException;
|
||||
}
|
||||
+126
-106
@@ -2,11 +2,9 @@ package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
@@ -38,12 +36,12 @@ import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDenoteForTracking
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogStartTrackingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogTrackingStateAnalyzer;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
|
||||
import com.sap.sailing.domain.abstractlog.shared.events.RegisterCompetitorEvent;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.CourseBase;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
@@ -54,7 +52,6 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.SharedDomainFactory;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.CourseDataImpl;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants;
|
||||
@@ -94,36 +91,39 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet)
|
||||
public RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet)
|
||||
throws NotDenotedForRaceLogTrackingException, Exception {
|
||||
RaceLog raceLog = raceColumn.getRaceLog(fleet);
|
||||
RaceLogTrackingState raceLogTrackingState = new RaceLogTrackingStateAnalyzer(raceLog).analyze();
|
||||
if (! raceLogTrackingState.isForTracking()) {
|
||||
if (!raceLogTrackingState.isForTracking()) {
|
||||
throw new NotDenotedForRaceLogTrackingException();
|
||||
}
|
||||
RegattaIdentifier regatta = ((RegattaLeaderboard) leaderboard).getRegatta().getRegattaIdentifier();
|
||||
|
||||
if (raceLogTrackingState != RaceLogTrackingState.TRACKING) {
|
||||
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createStartTrackingEvent(MillisecondsTimePoint.now(),
|
||||
service.getServerAuthor(), raceLog.getCurrentPassId());
|
||||
raceLog.add(event);
|
||||
}
|
||||
|
||||
if (! isRaceLogRaceTrackerAttached(service, raceLog)) {
|
||||
addTracker(service, regatta, leaderboard, raceColumn, fleet, -1);
|
||||
final RaceHandle result;
|
||||
if (!isRaceLogRaceTrackerAttached(service, raceLog)) {
|
||||
result = addTracker(service, regatta, leaderboard, raceColumn, fleet, -1);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link RaceLogRaceTracker}. If a {@link RaceLogStartTrackingEvent} is already present in the {@code RaceLog}
|
||||
* linked to the {@code raceColumn} and {@code fleet}, a {@code TrackedRace} is created immediately and tracking begins.
|
||||
* Otherwise, the {@code RaceLogRaceTracker} waits until a {@code StartTrackingEvent} is added to perform these actions.
|
||||
* The race first has to be denoted for racelog tracking.
|
||||
* Adds a {@link RaceLogRaceTracker}. If a {@link RaceLogStartTrackingEvent} is already present in the
|
||||
* {@code RaceLog} linked to the {@code raceColumn} and {@code fleet}, a {@code TrackedRace} is created immediately
|
||||
* and tracking begins. Otherwise, the {@code RaceLogRaceTracker} waits until a {@code StartTrackingEvent} is added
|
||||
* to perform these actions. The race first has to be denoted for racelog tracking.
|
||||
*/
|
||||
private RaceHandle addTracker(RacingEventService service, RegattaIdentifier regattaToAddTo, Leaderboard leaderboard,
|
||||
RaceColumn raceColumn, Fleet fleet, long timeoutInMilliseconds) throws RaceLogRaceTrackerExistsException, Exception {
|
||||
private RaceHandle addTracker(RacingEventService service, RegattaIdentifier regattaToAddTo,
|
||||
Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, long timeoutInMilliseconds)
|
||||
throws RaceLogRaceTrackerExistsException, Exception {
|
||||
RaceLog raceLog = raceColumn.getRaceLog(fleet);
|
||||
assert ! isRaceLogRaceTrackerAttached(service, raceLog) : new RaceLogRaceTrackerExistsException(
|
||||
assert !isRaceLogRaceTrackerAttached(service, raceLog) : new RaceLogRaceTrackerExistsException(
|
||||
leaderboard.getName() + " - " + raceColumn.getName() + " - " + fleet.getName());
|
||||
|
||||
Regatta regatta = regattaToAddTo == null ? null : service.getRegatta(regattaToAddTo);
|
||||
@@ -133,15 +133,16 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void denoteRaceForRaceLogTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn,
|
||||
Fleet fleet, String raceName) throws NotDenotableForRaceLogTrackingException {
|
||||
public void denoteRaceForRaceLogTracking(RacingEventService service, Leaderboard leaderboard,
|
||||
RaceColumn raceColumn, Fleet fleet, String raceName) throws NotDenotableForRaceLogTrackingException {
|
||||
|
||||
BoatClass boatClass = null;
|
||||
if (leaderboard instanceof RegattaLeaderboard) {
|
||||
RegattaLeaderboard rLeaderboard = (RegattaLeaderboard) leaderboard;
|
||||
boatClass = rLeaderboard.getRegatta().getBoatClass();
|
||||
} else {
|
||||
throw new NotDenotableForRaceLogTrackingException("Can only denote races in RegattaLeaderboards for RaceLog-tracking");
|
||||
throw new NotDenotableForRaceLogTrackingException(
|
||||
"Can only denote races in RegattaLeaderboards for RaceLog-tracking");
|
||||
}
|
||||
|
||||
if (raceName == null) {
|
||||
@@ -170,7 +171,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
for (Fleet fleet : column.getFleets()) {
|
||||
try {
|
||||
denoteRaceForRaceLogTracking(service, leaderboard, column, fleet, null);
|
||||
} catch (NotDenotableForRaceLogTrackingException e) {}
|
||||
} catch (NotDenotableForRaceLogTrackingException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,46 +187,9 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
return new RaceLogTrackingStateAnalyzer(raceColumn.getRaceLog(fleet)).analyze();
|
||||
}
|
||||
|
||||
private Waypoint duplicateWaypoint(Waypoint waypoint, Map<ControlPoint, ControlPoint> controlPointDuplicationCache,
|
||||
Map<Mark, Mark> markDuplicationCache, SharedDomainFactory baseDomainFactory) {
|
||||
ControlPoint oldCP = waypoint.getControlPoint();
|
||||
ControlPoint newCP = null;
|
||||
PassingInstruction pi = waypoint.getPassingInstructions();
|
||||
if (controlPointDuplicationCache.get(oldCP) != null) {
|
||||
newCP = controlPointDuplicationCache.get(oldCP);
|
||||
} else {
|
||||
Mark[] newMarks = new Mark[Util.size(oldCP.getMarks())];
|
||||
int i = 0;
|
||||
for (Mark oldMark : oldCP.getMarks()) {
|
||||
newMarks[i] = null;
|
||||
if (markDuplicationCache.get(oldMark) != null) {
|
||||
newMarks[i] = markDuplicationCache.get(oldMark);
|
||||
} else {
|
||||
newMarks[i] = baseDomainFactory.getOrCreateMark(UUID.randomUUID(), oldMark.getName(), oldMark.getType(),
|
||||
oldMark.getColor(), oldMark.getShape(), oldMark.getPattern());
|
||||
markDuplicationCache.put(oldMark, newMarks[i]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
switch (newMarks.length) {
|
||||
case 1:
|
||||
newCP = newMarks[0];
|
||||
break;
|
||||
case 2:
|
||||
newCP = baseDomainFactory.createControlPointWithTwoMarks(newMarks[0], newMarks[1], oldCP.getName());
|
||||
break;
|
||||
default:
|
||||
logger.log(Level.WARNING, "Don't know how to duplicate CP with more than 2 marks");
|
||||
throw new RuntimeException("Don't know how to duplicate CP with more than 2 marks");
|
||||
}
|
||||
controlPointDuplicationCache.put(oldCP, newCP);
|
||||
}
|
||||
|
||||
return baseDomainFactory.createWaypoint(newCP, pi);
|
||||
}
|
||||
|
||||
private void revokeAlreadyDefinedMarks(RaceLog raceLog, AbstractLogEventAuthor author) {
|
||||
List<RaceLogEvent> markEvents = new AllEventsOfTypeFinder<>(raceLog, true, RaceLogDefineMarkEvent.class).analyze();
|
||||
List<RaceLogEvent> markEvents = new AllEventsOfTypeFinder<>(raceLog, /* only unrevoked */ true, RaceLogDefineMarkEvent.class)
|
||||
.analyze();
|
||||
for (RaceLogEvent event : markEvents) {
|
||||
try {
|
||||
raceLog.revokeEvent(author, event, "removing mark that was already defined");
|
||||
@@ -235,70 +200,90 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyCourseAndCompetitors(RaceLog fromRaceLog, Set<RaceLog> toRaceLogs, SharedDomainFactory baseDomainFactory,
|
||||
RacingEventService service) {
|
||||
public void copyCourseAndCompetitors(RaceLog fromRaceLog, Set<RaceLog> toRaceLogs,
|
||||
SharedDomainFactory baseDomainFactory, RacingEventService service) {
|
||||
CourseBase course = new LastPublishedCourseDesignFinder(fromRaceLog).analyze();
|
||||
final Set<Mark> marks = new HashSet<>();
|
||||
course.getWaypoints().forEach(wp->Util.addAll(wp.getMarks(), marks));
|
||||
Set<Competitor> competitors = new RegisteredCompetitorsAnalyzer<>(fromRaceLog).analyze();
|
||||
|
||||
for (RaceLog toRaceLog : toRaceLogs) {
|
||||
if (course == null || ! new RaceLogTrackingStateAnalyzer(toRaceLog).analyze().isForTracking()) {
|
||||
if (course == null || !new RaceLogTrackingStateAnalyzer(toRaceLog).analyze().isForTracking()) {
|
||||
continue;
|
||||
}
|
||||
CourseBase to = new CourseDataImpl("Copy of \"" + course.getName());
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
int i = 0;
|
||||
Map<ControlPoint, ControlPoint> controlPointDuplicationCache = new HashMap<ControlPoint, ControlPoint>();
|
||||
revokeAlreadyDefinedMarks(toRaceLog, service.getServerAuthor());
|
||||
Map<Mark, Mark> markDuplicationCache = new HashMap<Mark, Mark>();
|
||||
for (Waypoint oldWaypoint : course.getWaypoints()) {
|
||||
to.addWaypoint(i++, duplicateWaypoint(oldWaypoint, controlPointDuplicationCache, markDuplicationCache, baseDomainFactory));
|
||||
to.addWaypoint(i++, oldWaypoint);
|
||||
}
|
||||
|
||||
for (Mark mark : markDuplicationCache.values()) {
|
||||
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createDefineMarkEvent(now, service.getServerAuthor(),
|
||||
toRaceLog.getCurrentPassId(), mark);
|
||||
for (Mark mark : marks) {
|
||||
RaceLogEvent event = RaceLogEventFactory.INSTANCE.createDefineMarkEvent(now,
|
||||
service.getServerAuthor(), toRaceLog.getCurrentPassId(), mark);
|
||||
toRaceLog.add(event);
|
||||
}
|
||||
|
||||
int passId = toRaceLog.getCurrentPassId();
|
||||
|
||||
RaceLogEvent newCourseEvent = RaceLogEventFactory.INSTANCE.createCourseDesignChangedEvent(
|
||||
now, service.getServerAuthor(), passId, to);
|
||||
RaceLogEvent newCourseEvent = RaceLogEventFactory.INSTANCE.createCourseDesignChangedEvent(now,
|
||||
service.getServerAuthor(), passId, to);
|
||||
toRaceLog.add(newCourseEvent);
|
||||
|
||||
registerCompetitors(service, toRaceLog, competitors);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private static interface DeviceMarkMappingEventFactory<VisitorT, EventT extends AbstractLogEvent<VisitorT>> {
|
||||
EventT createDeviceMarkMapping(DeviceIdentifier device, TimePoint timePoint);
|
||||
}
|
||||
|
||||
private <VisitorT, EventT extends AbstractLogEvent<VisitorT>, LogT extends AbstractLog<EventT, VisitorT>> void pingMark(
|
||||
LogT log, Mark mark, GPSFix gpsFix, RacingEventService service,
|
||||
DeviceMarkMappingEventFactory<VisitorT, EventT> factory, DeviceIdentifier device) {
|
||||
TimePoint time = gpsFix.getTimePoint();
|
||||
EventT mapping = factory.createDeviceMarkMapping(device, time);
|
||||
log.add(mapping);
|
||||
try {
|
||||
service.getGPSFixStore().storeFix(device, gpsFix);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not ping mark " + mark);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pingMark(RaceLog raceLog, Mark mark, GPSFix gpsFix, RacingEventService service) {
|
||||
DeviceIdentifier device = new PingDeviceIdentifierImpl();
|
||||
TimePoint time = gpsFix.getTimePoint();
|
||||
|
||||
RaceLogEvent mapping = RaceLogEventFactory.INSTANCE.createDeviceMarkMappingEvent(time,
|
||||
service.getServerAuthor(), device, mark, raceLog.getCurrentPassId(), time, time);
|
||||
raceLog.add(mapping);
|
||||
try {
|
||||
service.getGPSFixStore().storeFix(device, gpsFix);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not pint mark " + mark);
|
||||
pingMark(raceLog, mark, gpsFix, service,
|
||||
(DeviceIdentifier dev, TimePoint timePoint) -> RaceLogEventFactory.INSTANCE
|
||||
.createDeviceMarkMappingEvent(timePoint, service.getServerAuthor(), dev, mark,
|
||||
raceLog.getCurrentPassId(), timePoint, timePoint), new PingDeviceIdentifierImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pingMark(RegattaLog regattaLog, Mark mark, GPSFix gpsFix, RacingEventService service) {
|
||||
pingMark(
|
||||
regattaLog,
|
||||
mark,
|
||||
gpsFix,
|
||||
service,
|
||||
(DeviceIdentifier dev, TimePoint timePoint) -> new RegattaLogDeviceMarkMappingEventImpl(timePoint, service
|
||||
.getServerAuthor(), timePoint, UUID.randomUUID(), mark, dev, timePoint, timePoint), new PingDeviceIdentifierImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDenotationForRaceLogTracking(RacingEventService service, RaceLog raceLog) {
|
||||
RaceLogEvent denoteForTrackingEvent = new LastEventOfTypeFinder<>(raceLog, true, RaceLogDenoteForTrackingEvent.class).analyze();
|
||||
RaceLogEvent startTrackingEvent = new LastEventOfTypeFinder<>(raceLog, true, RaceLogStartTrackingEvent.class).analyze();
|
||||
RaceLogEvent denoteForTrackingEvent = new LastEventOfTypeFinder<>(raceLog, true,
|
||||
RaceLogDenoteForTrackingEvent.class).analyze();
|
||||
RaceLogEvent startTrackingEvent = new LastEventOfTypeFinder<>(raceLog, true, RaceLogStartTrackingEvent.class)
|
||||
.analyze();
|
||||
try {
|
||||
raceLog.revokeEvent(service.getServerAuthor(), denoteForTrackingEvent, "remove denotation");
|
||||
raceLog.revokeEvent(service.getServerAuthor(), startTrackingEvent, "reset start time upon removing denotation");
|
||||
raceLog.revokeEvent(service.getServerAuthor(), startTrackingEvent,
|
||||
"reset start time upon removing denotation");
|
||||
} catch (NotRevokableException e) {
|
||||
logger.log(Level.WARNING, "could not remove denotation by adding RevokeEvents", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT>
|
||||
void registerCompetitors(AbstractLogEventAuthor author, LogT log, Set<Competitor> competitors,
|
||||
private <LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT> void registerCompetitors(
|
||||
AbstractLogEventAuthor author, LogT log, Set<Competitor> competitors,
|
||||
Function<Competitor, EventT> registerEventFactory) {
|
||||
Set<Competitor> alreadyRegistered = new HashSet<Competitor>(new RegisteredCompetitorsAnalyzer<>(log).analyze());
|
||||
Set<Competitor> toBeRegistered = new HashSet<Competitor>();
|
||||
@@ -311,12 +296,12 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
toBeRemoved.removeAll(toBeRegistered);
|
||||
toBeRegistered.removeAll(alreadyRegistered);
|
||||
|
||||
//register
|
||||
// register
|
||||
for (Competitor c : toBeRegistered) {
|
||||
log.add(registerEventFactory.apply(c));
|
||||
}
|
||||
|
||||
//unregister
|
||||
// unregister
|
||||
for (EventT event : log.getUnrevokedEventsDescending()) {
|
||||
if (event instanceof RegisterCompetitorEvent) {
|
||||
RegisterCompetitorEvent<?> registerEvent = (RegisterCompetitorEvent<?>) event;
|
||||
@@ -334,7 +319,10 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
|
||||
@Override
|
||||
public void registerCompetitors(RacingEventService service, RaceLog raceLog, Set<Competitor> competitors) {
|
||||
registerCompetitors(service.getServerAuthor(), raceLog, competitors,
|
||||
registerCompetitors(
|
||||
service.getServerAuthor(),
|
||||
raceLog,
|
||||
competitors,
|
||||
c -> RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(),
|
||||
service.getServerAuthor(), raceLog.getCurrentPassId(), c));
|
||||
}
|
||||
@@ -347,8 +335,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
}
|
||||
|
||||
private MailService getMailService() {
|
||||
ServiceReference<MailService> ref = Activator.getContext()
|
||||
.getServiceReference(MailService.class);
|
||||
ServiceReference<MailService> ref = Activator.getContext().getServiceReference(MailService.class);
|
||||
if (ref == null) {
|
||||
logger.warning("No file storage management service registered");
|
||||
return null;
|
||||
@@ -361,7 +348,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
String serverUrlWithoutTrailingSlash, Set<Competitor> competitors, Locale locale) throws MailException {
|
||||
StringBuilder occuredExceptions = new StringBuilder();
|
||||
|
||||
for (Competitor competitor : competitors){
|
||||
for (Competitor competitor : competitors) {
|
||||
final String toAddress = competitor.getEmail();
|
||||
if (toAddress != null) {
|
||||
String leaderboardName = leaderboard.getName();
|
||||
@@ -370,17 +357,30 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
String url = DeviceMappingConstants.getDeviceMappingForRegattaLogUrl(serverUrlWithoutTrailingSlash,
|
||||
event.getId().toString(), leaderboardName, DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING,
|
||||
competitor.getId().toString(), NonGwtUrlHelper.INSTANCE);
|
||||
try {
|
||||
sendInvitationEmail(locale, toAddress, leaderboardName, competitorName, url);
|
||||
} catch (MailException e) {
|
||||
occuredExceptions.append(e.getMessage() + "\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(occuredExceptions.length() == 0)) {
|
||||
throw new MailException(occuredExceptions.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendInvitationEmail(Locale locale, final String toAddress, String leaderboardName, String invitee,
|
||||
String url) throws MailException {
|
||||
String subject = String.format("%s %s",
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "trackingInvitationFor"), competitorName);
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "trackingInvitationFor"), invitee);
|
||||
|
||||
// taken from http://www.tutorialspoint.com/javamail_api/javamail_api_send_inlineimage_in_email.htm
|
||||
BodyPart messageTextPart = new MimeBodyPart();
|
||||
String htmlText = String.format("<h1>%s %s</h1>" + "<p>%s <b>%s</b></p>"
|
||||
+ "<img src=\"cid:image\" title=\"%s\"><br/>"
|
||||
+ "<a href=\"%s\">%s</a>",
|
||||
+ "<img src=\"cid:image\" title=\"%s\"><br/>" + "<a href=\"%s\">%s</a>",
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "welcomeTo"), leaderboardName,
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "scanQRCodeOrVisitUrlToRegisterAs"), competitorName,
|
||||
url, url, RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "alternativelyVisitThisLink"));
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "scanQRCodeOrVisitUrlToRegisterAs"), invitee, url, url,
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "alternativelyVisitThisLink"));
|
||||
|
||||
try {
|
||||
messageTextPart.setContent(htmlText, "text/html");
|
||||
@@ -397,13 +397,33 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
|
||||
getMailService().sendMail(toAddress, subject, multipart);
|
||||
} catch (MessagingException | MailException | WriterException | IOException e) {
|
||||
logger.log(Level.SEVERE, "Error trying to send mail to " + competitor.getName()
|
||||
+ " with e-mail address " + toAddress, e);
|
||||
occuredExceptions.append(e.getMessage()+"\r\n");
|
||||
logger.log(Level.SEVERE, "Error trying to send mail to " + invitee + " with e-mail address " + toAddress, e);
|
||||
throw new MailException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inviteBuoyTenderViaEmail(Event event, Leaderboard leaderboard, String serverUrlWithoutTrailingSlash,
|
||||
String emails, Locale locale) throws MailException {
|
||||
|
||||
StringBuilder occuredExceptions = new StringBuilder();
|
||||
|
||||
String[] emailArray = emails.split(",");
|
||||
String leaderboardName = leaderboard.getName();
|
||||
|
||||
// http://<host>/buoy-tender/checkin&leaderboard_name=<leaderboard-name>
|
||||
String url = DeviceMappingConstants.getBuoyTenderInvitationUrl(serverUrlWithoutTrailingSlash, leaderboardName,
|
||||
NonGwtUrlHelper.INSTANCE);
|
||||
for (String toAddress : emailArray) {
|
||||
try {
|
||||
sendInvitationEmail(locale, toAddress, leaderboardName,
|
||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "buoyTender"), url);
|
||||
} catch (MailException e) {
|
||||
occuredExceptions.append(e.getMessage() + "\r\n");
|
||||
}
|
||||
if (!(occuredExceptions.length() == 0)){
|
||||
}
|
||||
|
||||
if (!(occuredExceptions.length() == 0)) {
|
||||
throw new MailException(occuredExceptions.toString());
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.base.Mark;
|
||||
|
||||
/**
|
||||
* Defines a mark, that can then be used to build a course.
|
||||
*
|
||||
* @author Fredrik Teschke
|
||||
*
|
||||
*/
|
||||
|
||||
+2
-3
@@ -1,7 +1,6 @@
|
||||
package com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
@@ -10,13 +9,13 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogAnalyzer;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDefineMarkEvent;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
|
||||
public class DefinedMarkFinder extends RaceLogAnalyzer<Collection<Mark>> {
|
||||
public class DefinedMarkFinder extends RaceLogAnalyzer<Iterable<Mark>> {
|
||||
public DefinedMarkFinder(RaceLog raceLog) {
|
||||
super(raceLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Mark> performAnalysis() {
|
||||
protected Iterable<Mark> performAnalysis() {
|
||||
List< Mark> result = new ArrayList<Mark>();
|
||||
for (RaceLogEvent event : getLog().getUnrevokedEvents()) {
|
||||
if (event instanceof RaceLogDefineMarkEvent) {
|
||||
|
||||
+6
@@ -48,6 +48,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
@@ -893,4 +894,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
public Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+5
@@ -587,4 +587,9 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -6,8 +6,8 @@ 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.Renamable;
|
||||
import com.sap.sailing.domain.regattalike.HasRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.IsRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface FlexibleLeaderboard extends Leaderboard, Renamable, HasRegattaLike, IsRegattaLike {
|
||||
public interface FlexibleLeaderboard extends LeaderboardThatHasRegattaLike, Renamable, IsRegattaLike {
|
||||
/**
|
||||
* Moves the column with the name <code>name</code> up.
|
||||
* @param name The name of the column to move.
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.leaderboard;
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.Renamable;
|
||||
import com.sap.sailing.domain.regattalike.HasRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
|
||||
public interface RegattaLeaderboard extends Leaderboard, Renamable, HasRegattaLike {
|
||||
public interface RegattaLeaderboard extends Renamable, LeaderboardThatHasRegattaLike {
|
||||
Regatta getRegatta();
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.sap.sailing.domain.regattalike;
|
||||
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
|
||||
public interface LeaderboardThatHasRegattaLike extends Leaderboard, HasRegattaLike {
|
||||
}
|
||||
@@ -221,9 +221,19 @@ public interface TrackedRace extends Serializable {
|
||||
/**
|
||||
* Yields the track describing <code>mark</code>'s movement over time; never <code>null</code> because a new track
|
||||
* will be created in case no track was present for <code>mark</code> so far.
|
||||
*
|
||||
* @see #getTrack(Mark)
|
||||
*/
|
||||
GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark);
|
||||
|
||||
/**
|
||||
* Yields the track describing <code>mark</code>'s movement over time; <code>null</code> if no track exists for
|
||||
* <code>mark</code> so far.
|
||||
*
|
||||
* @see #getOrCreateTrack(Mark)
|
||||
*/
|
||||
GPSFixTrack<Mark, GPSFix> getTrack(Mark mark);
|
||||
|
||||
/**
|
||||
* Retrieves all marks assigned to the race. They are not necessarily part of the race course.
|
||||
*/
|
||||
|
||||
+5
@@ -871,4 +871,9 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
}
|
||||
return new com.sap.sse.common.Util.Pair<Mark, Mark>(portMarkWhileApproachingLine, starboardMarkWhileApproachingLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicGPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
|
||||
return (DynamicGPSFixTrack<Mark, GPSFix>) super.getTrack(mark);
|
||||
}
|
||||
}
|
||||
+4
@@ -1092,4 +1092,8 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
|
||||
listener.speedAveragingChanged(oldMillis, millisecondsOverWhichToAverage);
|
||||
}
|
||||
}
|
||||
|
||||
public static Speed getDefaultMaxSpeedForSmoothing() {
|
||||
return DEFAULT_MAX_SPEED_FOR_SMOOTHING;
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -1280,6 +1280,15 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
*/
|
||||
@Override
|
||||
public GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark) {
|
||||
return getOrCreateTrack(mark, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
|
||||
return getOrCreateTrack(mark, false);
|
||||
}
|
||||
|
||||
private GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark, boolean createIfNotExistent){
|
||||
GPSFixTrack<Mark, GPSFix> result = markTracks.get(mark);
|
||||
if (result == null) {
|
||||
// try again, this time with more expensive synchronization
|
||||
@@ -1287,7 +1296,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
LockUtil.lockForRead(getSerializationLock());
|
||||
try {
|
||||
result = markTracks.get(mark);
|
||||
if (result == null) {
|
||||
if (result == null && createIfNotExistent) {
|
||||
result = createMarkTrack(mark);
|
||||
markTracks.put(mark, result);
|
||||
}
|
||||
|
||||
+4
@@ -357,6 +357,10 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Duration getDuration(Distance distance) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public SpeedWithBearing add(SpeedWithBearing other) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
@@ -46,6 +46,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
@@ -888,4 +889,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
entryPointModules=
|
||||
filesCopiedToWebInfLib=gwt-servlet.jar
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDQgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC5BdXRvUGxheTwvZW50cnktcG9pbnQtbW9kdWxlPjwvZ3d0LWNvbXBpbGUtc2V0dGluZ3M+
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDQgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuTGVhZGVyYm9hcmRFZGl0aW5nPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlJhY2VCb2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5TcGVjdGF0b3I8L2VudHJ5LXBvaW50LW1vZHVsZT48L2d3dC1jb21waWxlLXNldHRpbmdzPg\=\=
|
||||
Binary file not shown.
-5
@@ -263,11 +263,6 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement
|
||||
protected ListDataProvider<StrippedLeaderboardDTO> getListDataProvider() {
|
||||
return leaderboardList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue(StrippedLeaderboardDTO row) {
|
||||
return leaderboardTable.getSelectionModel().isSelected(row);
|
||||
}
|
||||
};
|
||||
selectionCheckboxColumn.setSortable(true);
|
||||
leaderboardColumnListHandler.setComparator(selectionCheckboxColumn, new Comparator<StrippedLeaderboardDTO>() {
|
||||
|
||||
+4
-2
@@ -88,8 +88,10 @@ public class AddDeviceMappingToRegattaLogDialog extends AbstractCancelableDialog
|
||||
|
||||
// load table content
|
||||
sailingService.getCompetitorRegistrations(leaderboardName, itemSelectionPanel.getSetCompetitorsCallback());
|
||||
// TODO marks from RegattaLog also?
|
||||
|
||||
// fetching marks definitions from the race logs; note that currently there are no mark *definitions*
|
||||
// on regatta logs although there can be mark/device assignments on a regatta log which then gives the
|
||||
// mark a position that applies across all races in the regatta-like thing
|
||||
sailingService.getMarksInRaceLogsAndTrackedRaces(leaderboardName, itemSelectionPanel.getSetMarksCallback());
|
||||
qrWidget = new DeviceMappingQRCodeWidget(stringMessages, new DeviceMappingQRCodeWidget.URLFactory() {
|
||||
@Override
|
||||
public String createURL(String baseUrlWithoutTrailingSlash, String mappedItemType, String mappedItemId)
|
||||
|
||||
+3
@@ -94,4 +94,7 @@ interface AdminConsoleResources extends ClientBundle {
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/close_time_range.png")
|
||||
ImageResource closeTimeRange();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/compose_mail_small.png")
|
||||
ImageResource inviteBuoyTenders();
|
||||
}
|
||||
+2
-2
@@ -84,9 +84,9 @@ public class ImportFixesAndAddMappingsDialog extends DataEntryDialog<Collection<
|
||||
errorReporter.reportError("Could not load competitors: " + caught.getMessage());
|
||||
}
|
||||
});
|
||||
sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback<Collection<MarkDTO>>() {
|
||||
sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback<Iterable<MarkDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<MarkDTO> result) {
|
||||
public void onSuccess(Iterable<MarkDTO> result) {
|
||||
markTable.refresh(result);
|
||||
}
|
||||
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.controls.GenericListBox;
|
||||
import com.sap.sse.gwt.client.controls.GenericListBox.ValueBuilder;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class InviteBuoyTenderDialog extends DataEntryDialog<Triple<EventDTO, String, String>> {
|
||||
private GenericListBox<EventDTO> events;
|
||||
private TextBox serverUrl;
|
||||
private TextBox emails;
|
||||
private StringMessages stringMessages;
|
||||
private SailingServiceAsync sailingService;
|
||||
private String leaderboardName;
|
||||
private ErrorReporter errorReporter;
|
||||
|
||||
public InviteBuoyTenderDialog(final StringMessages stringMessages, SailingServiceAsync sailingService, String leaderboardName, ErrorReporter errorReporter, DialogCallback<Triple<EventDTO, String, String>> callback) {
|
||||
super(stringMessages.selectEventForInvitation(), null, stringMessages.inviteCompetitors(), stringMessages
|
||||
.cancel(), new Validator<Triple<EventDTO, String, String>>() {
|
||||
@Override
|
||||
public String getErrorMessage(Triple<EventDTO, String, String> valueToValidate) {
|
||||
if (valueToValidate.getA() == null) {
|
||||
return stringMessages.pleaseSelectAnEvent();
|
||||
} else if (valueToValidate.getB() == null || valueToValidate.getB().isEmpty()) {
|
||||
return stringMessages.pleaseEnterA(stringMessages.hostname());
|
||||
} else if (valueToValidate.getC() == null || valueToValidate.getC().isEmpty()) {
|
||||
return stringMessages.pleaseEnterA(stringMessages.email())+stringMessages.orMultipleEmails() ;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, false, callback);
|
||||
this.stringMessages = stringMessages;
|
||||
this.sailingService = sailingService;
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.errorReporter = errorReporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Triple<EventDTO, String, String> getResult() {
|
||||
EventDTO event = events.getValue();
|
||||
String serverUrlString = serverUrl.getValue();
|
||||
String emailsString = emails.getValue();
|
||||
if (serverUrlString.endsWith("/")) {
|
||||
serverUrlString = serverUrlString.substring(0, serverUrlString.length() - 1);
|
||||
}
|
||||
return new Triple<>(event, serverUrlString, emailsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
Grid grid = new Grid(3, 3);
|
||||
grid.setWidget(0, 0, createLabel(stringMessages.hostname()));
|
||||
serverUrl = createTextBox(Window.Location.getProtocol() + "//" + Window.Location.getHost());
|
||||
emails = createTextBox("test@example.com,test2@example.com");
|
||||
grid.setWidget(0, 1, serverUrl);
|
||||
|
||||
grid.setWidget(1, 0, createLabel(stringMessages.event()));
|
||||
events = createGenericListBox(new ValueBuilder<EventDTO>() {
|
||||
@Override
|
||||
public String getValue(EventDTO item) {
|
||||
if (item == null) {
|
||||
return "";
|
||||
}
|
||||
return item.getName();
|
||||
}
|
||||
}, false);
|
||||
grid.setWidget(1, 1, events);
|
||||
|
||||
events.addItem((EventDTO) null);
|
||||
sailingService.getEventsForLeaderboard(leaderboardName, new AsyncCallback<Collection<EventDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<EventDTO> result) {
|
||||
events.addItems(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Could not load events: " + caught.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
grid.setWidget(2, 0, createLabel(stringMessages.email()));
|
||||
grid.setWidget(2, 1, emails);
|
||||
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -107,10 +107,10 @@ public class ItemToMapToDeviceSelectionPanel implements IsWidget {
|
||||
};
|
||||
}
|
||||
|
||||
public AsyncCallback<Collection<MarkDTO>> getSetMarksCallback() {
|
||||
return new AsyncCallback<Collection<MarkDTO>>() {
|
||||
public AsyncCallback<Iterable<MarkDTO>> getSetMarksCallback() {
|
||||
return new AsyncCallback<Iterable<MarkDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<MarkDTO> result) {
|
||||
public void onSuccess(Iterable<MarkDTO> result) {
|
||||
markTable.refresh(result);
|
||||
select(result, markTable.getSelectionModel());
|
||||
}
|
||||
|
||||
+4
-7
@@ -1,6 +1,5 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
@@ -93,10 +92,8 @@ public class MarkTableWrapper<S extends SelectionModel<MarkDTO>> extends TableWr
|
||||
table.addColumn(markUUIDColumn, "UUID");
|
||||
}
|
||||
|
||||
public void refresh(Collection<MarkDTO> marks) {
|
||||
dataProvider.getList().clear();
|
||||
dataProvider.getList().addAll(marks);
|
||||
dataProvider.flush();
|
||||
public void refresh(Iterable<MarkDTO> marks) {
|
||||
super.refresh(marks);
|
||||
Collections.sort(dataProvider.getList(), new Comparator<MarkDTO>() {
|
||||
@Override
|
||||
public int compare(MarkDTO o1, MarkDTO o2) {
|
||||
@@ -106,9 +103,9 @@ public class MarkTableWrapper<S extends SelectionModel<MarkDTO>> extends TableWr
|
||||
}
|
||||
|
||||
public void refresh(String leaderboardName, String raceColumnName, String fleetName) {
|
||||
sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback<Collection<MarkDTO>>() {
|
||||
sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback<Iterable<MarkDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<MarkDTO> result) {
|
||||
public void onSuccess(Iterable<MarkDTO> result) {
|
||||
refresh(result);
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.cell.client.FieldUpdater;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
@@ -125,9 +123,9 @@ public class RaceLogTrackingCourseDefinitionDialog extends RaceLogTrackingDialog
|
||||
}
|
||||
});
|
||||
|
||||
sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback<Collection<MarkDTO>>() {
|
||||
sailingService.getMarksInRaceLog(leaderboardName, raceColumnName, fleetName, new AsyncCallback<Iterable<MarkDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<MarkDTO> result) {
|
||||
public void onSuccess(Iterable<MarkDTO> result) {
|
||||
marks.refresh(result);
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell {
|
||||
public static final String ACTION_DENOTE_FOR_RACELOG_TRACKING = "ACTION_DENOTE_FOR_RACELOG_TRACKING";
|
||||
public final static String ACTION_COMPETITOR_REGISTRATIONS = "ACTION_COMPETITOR_REGISTRATIONS";
|
||||
public final static String ACTION_MAP_DEVICES = "ACTION_MAP_DEVICES";
|
||||
public final static String ACTION_INVITE_BUOY_TENDERS = "ACTION_INVITE_BUOY_TENDERS";
|
||||
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
@@ -34,6 +35,8 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell {
|
||||
makeImagePrototype(resources.competitorRegistrations())));
|
||||
result.add(new ImageSpec(ACTION_MAP_DEVICES, stringMessages.mapDevices(),
|
||||
makeImagePrototype(resources.mapDevices())));
|
||||
result.add(new ImageSpec(ACTION_INVITE_BUOY_TENDERS, stringMessages.inviteBuoyTenders(),
|
||||
makeImagePrototype(resources.inviteBuoyTenders())));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+45
-6
@@ -9,6 +9,7 @@ import com.google.gwt.cell.client.FieldUpdater;
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.LocaleInfo;
|
||||
import com.google.gwt.user.cellview.client.CellTable;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
@@ -16,6 +17,7 @@ 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.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.view.client.SelectionChangeEvent;
|
||||
@@ -31,9 +33,11 @@ import com.sap.sailing.gwt.ui.client.RegattaRefresher;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
|
||||
@@ -44,6 +48,8 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
|
||||
public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConfigPanel implements LeaderboardsDisplayer {
|
||||
private Button startTrackingButton;
|
||||
private TrackFileImportDeviceIdentifierTableWrapper deviceIdentifierTable;
|
||||
private CheckBox correctWindDirectionForDeclination;
|
||||
private CheckBox trackWind;
|
||||
|
||||
public RaceLogTrackingEventManagementPanel(SailingServiceAsync sailingService,
|
||||
RegattaRefresher regattaRefresher, LeaderboardsRefresher leaderboardsRefresher,
|
||||
@@ -132,6 +138,8 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf
|
||||
} else if (RaceLogTrackingEventManagementImagesBarCell.ACTION_MAP_DEVICES.equals(value)) {
|
||||
new AddDeviceMappingToRegattaLogDialog(sailingService, errorReporter, stringMessages,
|
||||
leaderboardName).show();
|
||||
} else if (RaceLogTrackingEventManagementImagesBarCell.ACTION_INVITE_BUOY_TENDERS.equals(value)) {
|
||||
openChooseEventDialogAndSendMails(leaderboardName);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -287,13 +295,17 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf
|
||||
|
||||
@Override
|
||||
protected void addSelectedLeaderboardRacesControls(Panel racesPanel) {
|
||||
trackWind = new CheckBox(stringMessages.trackWind());
|
||||
correctWindDirectionForDeclination = new CheckBox(stringMessages.declinationCheckbox());
|
||||
startTrackingButton = new Button(stringMessages.startTracking());
|
||||
startTrackingButton.setEnabled(false);
|
||||
racesPanel.add(trackWind);
|
||||
racesPanel.add(correctWindDirectionForDeclination);
|
||||
racesPanel.add(startTrackingButton);
|
||||
startTrackingButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
startTracking(raceColumnTableSelectionModel.getSelectedSet());
|
||||
startTracking(raceColumnTableSelectionModel.getSelectedSet(), trackWind.getValue(), correctWindDirectionForDeclination.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -393,13 +405,12 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf
|
||||
});
|
||||
}
|
||||
|
||||
private void startTracking(Set<RaceColumnDTOAndFleetDTOWithNameBasedEquality> races) {
|
||||
private void startTracking(Set<RaceColumnDTOAndFleetDTOWithNameBasedEquality> races, boolean trackWind, boolean correctWindByDeclination) {
|
||||
final StrippedLeaderboardDTO leaderboard = getSelectedLeaderboard();
|
||||
|
||||
//prompt user if competitor registrations are missing for same races
|
||||
String namesOfRacesMissingRegistrations = "";
|
||||
for (RaceColumnDTOAndFleetDTOWithNameBasedEquality race : races) {
|
||||
if (! doCompetitorResgistrationsExist(race)) {
|
||||
if (!doCompetitorResgistrationsExist(race)) {
|
||||
namesOfRacesMissingRegistrations += race.getA().getName() + "/" + race.getB().getName() + " ";
|
||||
}
|
||||
}
|
||||
@@ -410,12 +421,11 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (RaceColumnDTOAndFleetDTOWithNameBasedEquality race : races) {
|
||||
final RaceColumnDTO raceColumn = race.getA();
|
||||
final FleetDTO fleet = race.getB();
|
||||
|
||||
sailingService.startRaceLogTracking(leaderboard.name, raceColumn.getName(), fleet.getName(),
|
||||
trackWind, correctWindByDeclination,
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
@@ -458,4 +468,33 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf
|
||||
public void cancel() { }
|
||||
}).show();
|
||||
}
|
||||
|
||||
private String getLocaleInfo() {
|
||||
return LocaleInfo.getCurrentLocale().getLocaleName();
|
||||
}
|
||||
|
||||
private void openChooseEventDialogAndSendMails(final String leaderBoardName) {
|
||||
new InviteBuoyTenderDialog(stringMessages, sailingService, leaderBoardName, errorReporter, new DialogCallback<Triple<EventDTO, String, String>>() {
|
||||
@Override
|
||||
public void ok(Triple<EventDTO, String, String> result) {
|
||||
sailingService.inviteBuoyTenderViaEmail(result.getB(), result.getA(), leaderBoardName, result.getC(), getLocaleInfo(), new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Window.alert(stringMessages.sendingMailsFailed() + caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
Window.alert(stringMessages.sendingMailsSuccessfull());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
|
||||
}
|
||||
}).show();
|
||||
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,7 +1,5 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.cellview.client.CellTable;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
@@ -15,6 +13,7 @@ import com.google.gwt.view.client.SingleSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
/**
|
||||
@@ -95,9 +94,9 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
|
||||
return dataProvider;
|
||||
}
|
||||
|
||||
public void refresh(Collection<T> newItems) {
|
||||
public void refresh(Iterable<T> newItems) {
|
||||
dataProvider.getList().clear();
|
||||
dataProvider.getList().addAll(newItems);
|
||||
Util.addAll(newItems, dataProvider.getList());
|
||||
dataProvider.flush();
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -458,7 +458,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
*
|
||||
* @see RaceLogTrackingAdapter#startTracking
|
||||
*/
|
||||
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName)
|
||||
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, boolean trackWind, boolean correctWindByDeclination)
|
||||
throws NotDenotedForRaceLogTrackingException, Exception;
|
||||
|
||||
void setCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName, Set<CompetitorDTO> competitors);
|
||||
@@ -477,7 +477,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
|
||||
void addMarkToRaceLog(String leaderboardName, String raceColumnName, String fleetName, MarkDTO markDTO);
|
||||
|
||||
Collection<MarkDTO> getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName);
|
||||
Iterable<MarkDTO> getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName);
|
||||
|
||||
/**
|
||||
* Adds the course definition to the racelog, while trying to reuse existing marks, controlpoints and waypoints
|
||||
@@ -492,6 +492,10 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
*/
|
||||
void pingMarkViaRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, MarkDTO mark, PositionDTO position);
|
||||
|
||||
/**
|
||||
* @param raceLogFrom identifies the race log to copy from by its leaderboard name, race column name and fleet name
|
||||
* @param raceLogsTo identifies the race log to copy from by their leaderboard name, race column name and fleet name
|
||||
*/
|
||||
void copyCourseAndCompetitorsToOtherRaceLogs(Util.Triple<String, String, String> raceLogFrom,
|
||||
Set<Util.Triple<String, String, String>> raceLogsTo);
|
||||
|
||||
@@ -577,4 +581,9 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
|
||||
void inviteCompetitorsForTrackingViaEmail(String serverUrlWithoutTrailingSlash, EventDTO event,
|
||||
String leaderboardName, Set<CompetitorDTO> competitors, String localeInfo) throws MailException;
|
||||
|
||||
Iterable<MarkDTO> getMarksInRaceLogsAndTrackedRaces(String leaderboardName);
|
||||
|
||||
void inviteBuoyTenderViaEmail(String serverUrlWithoutTrailingSlash, EventDTO eventDto, String leaderboardName,
|
||||
String emails, String localeInfoName) throws MailException;
|
||||
}
|
||||
+8
-3
@@ -571,8 +571,8 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM
|
||||
|
||||
void denoteForRaceLogTracking(String leaderboardName, AsyncCallback<Void> callback);
|
||||
|
||||
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName,
|
||||
AsyncCallback<Void> callback);
|
||||
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, boolean trackWind,
|
||||
boolean correctWindByDeclination, AsyncCallback<Void> callback);
|
||||
|
||||
/**
|
||||
* Set the competitor registrations in the racelog. Unregisters formerly registered competitors
|
||||
@@ -595,7 +595,7 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM
|
||||
AsyncCallback<Void> callback);
|
||||
|
||||
void getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName,
|
||||
AsyncCallback<Collection<MarkDTO>> callback);
|
||||
AsyncCallback<Iterable<MarkDTO>> callback);
|
||||
|
||||
void addCourseDefinitionToRaceLog(String leaderboardName, String raceColumnName, String fleetName,
|
||||
List<Util.Pair<ControlPointDTO, PassingInstruction>> course, AsyncCallback<Void> callback);
|
||||
@@ -688,4 +688,9 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM
|
||||
|
||||
void inviteCompetitorsForTrackingViaEmail(String serverUrlWithoutTrailingSlash, EventDTO event,
|
||||
String leaderboardName, Set<CompetitorDTO> competitors, String localeInfo, AsyncCallback<Void> callback);
|
||||
|
||||
void getMarksInRaceLogsAndTrackedRaces(String leaderboardName, AsyncCallback<Iterable<MarkDTO>> callback);
|
||||
|
||||
void inviteBuoyTenderViaEmail(String serverUrlWithoutTrailingSlash, EventDTO eventDto, String leaderboardName,
|
||||
String emails, String localeInfoName, AsyncCallback<Void> callback);
|
||||
}
|
||||
+2
@@ -1162,6 +1162,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
|
||||
String showCompetitorFullNameColumn();
|
||||
String loadingDimensionValues();
|
||||
String runningQuery();
|
||||
String inviteBuoyTenders();
|
||||
String orMultipleEmails();
|
||||
String courseOverGroundTrueDegreesTooltip();
|
||||
String courseOverGroundTrueDegrees();
|
||||
String distanceIncludingGateStartInMeters();
|
||||
|
||||
+2
@@ -1142,6 +1142,8 @@ showCompetitorSailIdColumn=Show Competitor Sail Id
|
||||
showCompetitorFullNameColumn=Show Competitor Full Name
|
||||
loadingDimensionValues=Loading dimension values
|
||||
runningQuery=Running Query
|
||||
inviteBuoyTenders=Invite Buoy Tenders
|
||||
orMultipleEmails=or multiple E-Mails separated by comma
|
||||
courseOverGroundTrueDegreesTooltip=True course over ground in degrees
|
||||
courseOverGroundTrueDegrees=COG
|
||||
distanceIncludingGateStartInMeters=Distance (w/ Gate Start)
|
||||
|
||||
+2
@@ -1132,6 +1132,8 @@ showCompetitorSailIdColumn=Sail Id anzeigen
|
||||
showCompetitorFullNameColumn=Vollen Namen anzeigen
|
||||
loadingDimensionValues=Lade Dimensionswerte
|
||||
runningQuery=Datenabfrage läuft
|
||||
inviteBuoyTenders=Tonnenleger einladen
|
||||
orMultipleEmails=oder mehrere E-Mails (Komma-separiert)
|
||||
courseOverGroundTrueDegreesTooltip=Rechtweisender Kurs über Grund in Grad
|
||||
courseOverGroundTrueDegrees=KüG
|
||||
distanceIncludingGateStartInMeters=Distanz (m. Torstart)
|
||||
|
||||
+2
@@ -1081,6 +1081,8 @@ showCompetitorSailIdColumn=Show Competitor Sail Id
|
||||
showCompetitorFullNameColumn=Show Competitor Full Name
|
||||
loadingDimensionValues=Loading dimension values
|
||||
runningQuery=Running Query
|
||||
inviteBuoyTenders=Invite Buoy Tenders
|
||||
orMultipleEmails=or multiple E-Mails separated by comma
|
||||
courseOverGroundTrueDegreesTooltip=True course over ground in degrees
|
||||
courseOverGroundTrueDegrees=COG
|
||||
distanceIncludingGateStartInMeters=Distance (w/ Gate Start)
|
||||
|
||||
+59
-23
@@ -71,14 +71,12 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.gate.Readon
|
||||
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.rrs26.ReadonlyRRS26RacingProcedure;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogCloseOpenEndedDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.DefinedMarkFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogLastPingMappingEventFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogOpenEndedDeviceMappingCloser;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogTrackingStateAnalyzer;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.DeviceCompetitorMappingFinder;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.DeviceMarkMappingFinder;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
|
||||
import com.sap.sailing.domain.abstractlog.shared.events.DeviceMappingEvent;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
@@ -220,6 +218,7 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl;
|
||||
import com.sap.sailing.domain.regattalike.HasRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
import com.sap.sailing.domain.regattalog.RegattaLogStore;
|
||||
import com.sap.sailing.domain.swisstimingadapter.StartList;
|
||||
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
|
||||
@@ -253,7 +252,6 @@ import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
@@ -4884,28 +4882,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
|
||||
Serializable id = UUID.randomUUID();
|
||||
|
||||
return baseDomainFactory.getOrCreateMark(id, dto.getName(), dto.type, dto.color, dto.shape, dto.pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Also finds the last position of the marks, if set by pinging them
|
||||
*/
|
||||
private Collection<MarkDTO> convertToMarkDTOs(RaceLog raceLog, Iterable<Mark> marks) {
|
||||
private Collection<MarkDTO> convertToMarkDTOs(LeaderboardThatHasRegattaLike leaderboard, RaceLog raceLog, Iterable<Mark> marks) {
|
||||
List<MarkDTO> dtos = new ArrayList<MarkDTO>();
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
for (Mark mark : marks) {
|
||||
DynamicGPSFixTrackImpl<Mark> track = new DynamicGPSFixTrackImpl<Mark>(mark, 0);
|
||||
DeviceMappingEvent<?, Mark> lastPingMappingEvent = new RaceLogLastPingMappingEventFinder<Mark>(raceLog, mark).analyze();
|
||||
if (lastPingMappingEvent != null) {
|
||||
try {
|
||||
getService().getGPSFixStore().loadMarkTrack(track, DeviceMappingImpl.convertToDeviceMapping(lastPingMappingEvent));
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not convert to Mark DTO: " + mark);
|
||||
}
|
||||
}
|
||||
Position lastPos = track.getLastRawFix() == null ? null : track.getLastRawFix().getPosition();
|
||||
final Position lastPos = getService().getMarkPosition(mark, leaderboard, now, raceLog);
|
||||
dtos.add(convertToMarkDTO(mark, lastPos));
|
||||
}
|
||||
return dtos;
|
||||
@@ -4921,10 +4909,39 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MarkDTO> getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName) {
|
||||
public Iterable<MarkDTO> getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName) {
|
||||
RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
|
||||
Collection<Mark> marks = new DefinedMarkFinder(raceLog).analyze();
|
||||
return convertToMarkDTOs(raceLog, marks);
|
||||
Iterable<Mark> marks = new DefinedMarkFinder(raceLog).analyze();
|
||||
return convertToMarkDTOs((LeaderboardThatHasRegattaLike) getService().getLeaderboardByName(leaderboardName), raceLog, marks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<MarkDTO> getMarksInRaceLogsAndTrackedRaces(String leaderboardName) {
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard == null) {
|
||||
// TODO: implement proper Exception Handling
|
||||
return null;
|
||||
}
|
||||
Set<MarkDTO> markDTOs = new HashSet<>();
|
||||
Map<Serializable, Mark> marksById = new HashMap<>();
|
||||
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
|
||||
for (Fleet fleet : raceColumn.getFleets()) {
|
||||
RaceLog raceLog = raceColumn.getRaceLog(fleet);
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); //might not yet be attached
|
||||
for (Mark markDefinitionFromRaceLog : new DefinedMarkFinder(raceLog).analyze()) {
|
||||
marksById.put(markDefinitionFromRaceLog.getId(), markDefinitionFromRaceLog);
|
||||
}
|
||||
if (trackedRace != null) {
|
||||
for (Mark markFromTrackedRace : trackedRace.getMarks()) {
|
||||
marksById.put(markFromTrackedRace.getId(), markFromTrackedRace);
|
||||
}
|
||||
}
|
||||
Util.addAll(convertToMarkDTOs(
|
||||
(LeaderboardThatHasRegattaLike) getService().getLeaderboardByName(leaderboardName),
|
||||
raceLog, marksById.values()), markDTOs);
|
||||
}
|
||||
}
|
||||
return markDTOs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -5011,7 +5028,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
for (com.sap.sse.common.Util.Triple<String, String, String> toTriple : toTriples) {
|
||||
toRaceLogs.add(getRaceLog(toTriple));
|
||||
}
|
||||
|
||||
getRaceLogTrackingAdapter().copyCourseAndCompetitors(fromRaceLog, toRaceLogs, baseDomainFactory, getService());
|
||||
}
|
||||
|
||||
@@ -5195,13 +5211,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName)
|
||||
public void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, final boolean trackWind, final boolean correctWindByDeclination)
|
||||
throws NotDenotedForRaceLogTrackingException, Exception {
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName);
|
||||
Fleet fleet = raceColumn.getFleetByName(fleetName);
|
||||
|
||||
getRaceLogTrackingAdapter().startTracking(getService(), leaderboard, raceColumn, fleet);
|
||||
final RaceHandle raceHandle = getRaceLogTrackingAdapter().startTracking(getService(), leaderboard, raceColumn, fleet);
|
||||
if (raceHandle != null && trackWind) {
|
||||
new Thread("Wind tracking starter for race " + leaderboardName + "/" + raceColumnName + "/" + fleetName) {
|
||||
public void run() {
|
||||
try {
|
||||
startTrackingWind(raceHandle, correctWindByDeclination,
|
||||
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -5435,4 +5462,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
getRaceLogTrackingAdapter().inviteCompetitorsForTrackingViaEmail(event, leaderboard, serverUrlWithoutTrailingSlash,
|
||||
competitors, getLocale(localeInfoName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inviteBuoyTenderViaEmail(String serverUrlWithoutTrailingSlash, EventDTO eventDto,
|
||||
String leaderboardName, String emails, String localeInfoName) throws MailException {
|
||||
Event event = getService().getEvent(eventDto.id);
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
getRaceLogTrackingAdapter().inviteBuoyTenderViaEmail(event, leaderboard, serverUrlWithoutTrailingSlash,
|
||||
emails, getLocale(localeInfoName));
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 270 B |
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sailing.server.gateway.deserialization.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.FlatSmartphoneUuidAndGPSFixMovingJsonSerializer;
|
||||
|
||||
public class FlatGPSFixJsonDeserializer implements
|
||||
JsonDeserializer<GPSFix> {
|
||||
|
||||
@Override
|
||||
public GPSFix deserialize(JSONObject object) throws JsonDeserializationException {
|
||||
try {
|
||||
double lonDeg = Double.parseDouble(object.get(FlatSmartphoneUuidAndGPSFixMovingJsonSerializer.LON_DEG).toString());
|
||||
double latDeg = Double.parseDouble(object.get(FlatSmartphoneUuidAndGPSFixMovingJsonSerializer.LAT_DEG).toString());
|
||||
long timeMillis = Long.parseLong(object.get(FlatSmartphoneUuidAndGPSFixMovingJsonSerializer.TIME_MILLIS).toString());
|
||||
return GPSFixImpl.create(lonDeg, latDeg, timeMillis);
|
||||
} catch (NumberFormatException e){
|
||||
throw new JsonDeserializationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.sap.sailing.server.gateway.serialization.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
|
||||
public class FlatGPSFixJsonSerializer implements JsonSerializer<GPSFix> {
|
||||
public static final String FIELD_LON_DEG = "longitude";
|
||||
public static final String FIELD_LAT_DEG = "latitude";
|
||||
public static final String FIELD_TIME_MILLIS = "timestamp";
|
||||
public static final String FIELD_ACCURACY = "accuracy";
|
||||
|
||||
@Override
|
||||
public JSONObject serialize(GPSFix object) {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put(FIELD_LON_DEG, object.getPosition().getLngDeg());
|
||||
result.put(FIELD_LAT_DEG, object.getPosition().getLatDeg());
|
||||
result.put(FIELD_TIME_MILLIS, object.getTimePoint().asMillis());
|
||||
// TODO the default accuracy is 1.0; when fixes ever have an accuracy, report it here
|
||||
result.put(FIELD_ACCURACY, 1.0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.sap.sailing.server.gateway.serialization.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class MarkJsonSerializerWithPosition implements JsonSerializer<Pair<ControlPoint, Position>> {
|
||||
public static final String FIELD_POSITION = "position";
|
||||
|
||||
private final FlatGPSFixJsonSerializer gpsFixSerializer;
|
||||
private final MarkJsonSerializer markSerializer;
|
||||
|
||||
public MarkJsonSerializerWithPosition(MarkJsonSerializer markSerializer, FlatGPSFixJsonSerializer gpsFixSerializer) {
|
||||
this.markSerializer = markSerializer;
|
||||
this.gpsFixSerializer = gpsFixSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject serialize(Pair<ControlPoint, Position> object) {
|
||||
final JSONObject result = markSerializer.serialize(object.getA());
|
||||
result.put(FIELD_POSITION,
|
||||
object.getB() == null ? null : gpsFixSerializer.serialize(new GPSFixImpl(object.getB(),
|
||||
MillisecondsTimePoint.now())));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package com.sap.sailing.server.gateway.jaxrs.api;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.DeviceMarkMappingFinder;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
|
||||
import com.sap.sailing.server.gateway.test.jaxrs.AbstractJaxRsApiTest;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class LeaderboardsResourcePingMarkTest extends AbstractJaxRsApiTest {
|
||||
private Mark mark;
|
||||
private RegattaLog log;
|
||||
private RegattaLeaderboard leaderboard;
|
||||
|
||||
public static final String PING_MARK_JSON =
|
||||
" {\n" +
|
||||
" \"timestamp\" : 1427142552000,\n" +
|
||||
" \"latitude\" : 54.325246,\n" +
|
||||
" \"longitude\" : 10.148556,\n" +
|
||||
" \"speed\" : 3.61,\n" +
|
||||
" \"course\" : 258.11,\n" +
|
||||
" }";
|
||||
|
||||
public static final String PING2_MARK_JSON =
|
||||
" {\n" +
|
||||
" \"timestamp\" : 1427142562000,\n" +
|
||||
" \"latitude\" : 54.425246,\n" +
|
||||
" \"longitude\" : 10.248556,\n" +
|
||||
" \"speed\" : 3.61,\n" +
|
||||
" \"course\" : 258.11,\n" +
|
||||
" }";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
mark = racingEventService.getBaseDomainFactory().getOrCreateMark("id", "name");
|
||||
Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(),
|
||||
MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, Collections
|
||||
.singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false,
|
||||
new HighPoint(), 0, null);
|
||||
racingEventService.addRegattaWithoutReplication(regatta);
|
||||
leaderboard = racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "regatta", new int[] {});
|
||||
|
||||
log = leaderboard.getRegattaLike().getRegattaLog();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckinAndCheckout() throws Exception {
|
||||
LeaderboardsResource resource = spyResource(new LeaderboardsResource());
|
||||
doReturn(RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(racingEventService.getBaseDomainFactory())).when(resource).getRaceLogTrackingAdapter();
|
||||
{
|
||||
Response response = resource.pingMark(PING_MARK_JSON, leaderboard.getName(), mark.getId().toString());
|
||||
assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode()));
|
||||
Map<Mark, List<DeviceMapping<Mark>>> mappings = new DeviceMarkMappingFinder<>(log).analyze();
|
||||
List<DeviceMapping<Mark>> mappingsForMark = mappings.get(mark);
|
||||
assertThat("one mapping was created for the one ping", mappingsForMark.size(), equalTo(1));
|
||||
assertOneFixPerMapping(mappingsForMark);
|
||||
}
|
||||
{
|
||||
// now produce a second ping; this should produce two fixes for the mark: one repeating the last known
|
||||
// position, and another one with the new position.
|
||||
Response response = resource.pingMark(PING2_MARK_JSON, leaderboard.getName(), mark.getId().toString());
|
||||
assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode()));
|
||||
Map<Mark, List<DeviceMapping<Mark>>> mappings = new DeviceMarkMappingFinder<>(log).analyze();
|
||||
List<DeviceMapping<Mark>> mappingsForMark = mappings.get(mark);
|
||||
assertThat("two additional mappings were created for the second ping", mappingsForMark.size(), equalTo(3));
|
||||
assertOneFixPerMapping(mappingsForMark);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertOneFixPerMapping(List<DeviceMapping<Mark>> mappingsForMark) throws TransformationException,
|
||||
NoCorrespondingServiceRegisteredException {
|
||||
for (DeviceMapping<Mark> i : mappingsForMark) {
|
||||
DeviceIdentifier device = i.getDevice();
|
||||
// first ping produces exactly one fix:
|
||||
assertThat("all fixes stored", racingEventService.getGPSFixStore().getNumberOfFixes(device), equalTo(1L));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
||||
import com.sap.sailing.domain.base.impl.NationalityImpl;
|
||||
import com.sap.sailing.domain.base.impl.PersonImpl;
|
||||
import com.sap.sailing.domain.base.impl.TeamImpl;
|
||||
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneUuidServiceFinderFactory;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
@@ -40,7 +41,7 @@ public abstract class AbstractJaxRsApiTest {
|
||||
service = MongoDBConfiguration.getDefaultTestConfiguration().getService();
|
||||
service.getDB().dropDatabase();
|
||||
|
||||
racingEventService = new RacingEventServiceImpl();
|
||||
racingEventService = new RacingEventServiceImpl(true, new MockSmartphoneUuidServiceFinderFactory());
|
||||
}
|
||||
|
||||
protected <T extends AbstractSailingServerResource> T spyResource(T resource) {
|
||||
|
||||
+14
-11
@@ -21,16 +21,7 @@ import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
|
||||
public class GPSFixesResourceTest {
|
||||
private RacingEventService service;
|
||||
@Before
|
||||
public void setup() {
|
||||
service = new RacingEventServiceImpl(true, new MockSmartphoneUuidServiceFinderFactory());
|
||||
service.getMongoObjectFactory().getDatabase().dropDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserialize() throws ParseException, TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
String json = "{\n" +
|
||||
public static final String FIXES_JSON = "{\n" +
|
||||
" \"deviceUuid\" : \"af855a56-9726-4a9c-a77e-da955bd289bf\",\n" +
|
||||
" \"fixes\" : [\n" +
|
||||
" {\n" +
|
||||
@@ -49,12 +40,24 @@ public class GPSFixesResourceTest {
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
|
||||
private RacingEventService service;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
service = new RacingEventServiceImpl(true, new MockSmartphoneUuidServiceFinderFactory());
|
||||
service.getMongoObjectFactory().getDatabase().dropDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserialize() throws ParseException, TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
|
||||
GPSFixesResource resource = new GPSFixesResource() {
|
||||
public RacingEventService getService() {
|
||||
return service;
|
||||
};
|
||||
};
|
||||
Response response = resource.postFixes(json);
|
||||
Response response = resource.postFixes(FIXES_JSON);
|
||||
assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode()));
|
||||
|
||||
DeviceIdentifier device = new SmartphoneUUIDIdentifierImpl(UUID.fromString("af855a56-9726-4a9c-a77e-da955bd289bf"));
|
||||
|
||||
@@ -24,6 +24,7 @@ Require-Bundle: com.sap.sailing.domain,
|
||||
com.sap.sailing.domain.tractracadapter,
|
||||
com.sap.sailing.domain.persistence,
|
||||
com.sap.sse.mongodb,
|
||||
com.sap.sailing.domain.racelogtrackingadapter,
|
||||
org.mongodb.mongo-java-driver;bundle-version="2.13.0",
|
||||
javax.servlet;bundle-version="2.6.0",
|
||||
com.sap.sailing.domain.common,
|
||||
|
||||
+11
@@ -6,6 +6,7 @@ import java.math.RoundingMode;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.core.Context;
|
||||
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
@@ -21,6 +22,16 @@ import com.sap.sse.util.DateParser;
|
||||
public abstract class AbstractSailingServerResource {
|
||||
@Context ServletContext servletContext;
|
||||
|
||||
protected <T> T getService(Class<T> clazz) {
|
||||
BundleContext context = (BundleContext) servletContext
|
||||
.getAttribute(RestServletContainer.OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME);
|
||||
ServiceTracker<T, T> tracker = new ServiceTracker<T, T>(context, clazz, null);
|
||||
tracker.open();
|
||||
T service = tracker.getService();
|
||||
tracker.close();
|
||||
return service;
|
||||
}
|
||||
|
||||
public RacingEventService getService() {
|
||||
@SuppressWarnings("unchecked")
|
||||
ServiceTracker<RacingEventService, RacingEventService> tracker = (ServiceTracker<RacingEventService, RacingEventService>) servletContext.getAttribute(RestServletContainer.RACING_EVENT_SERVICE_TRACKER_NAME);
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ import com.sun.jersey.spi.container.servlet.ServletContainer;
|
||||
public class RestServletContainer extends ServletContainer {
|
||||
private static final long serialVersionUID = -3132655822281021061L;
|
||||
|
||||
private static final String OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME = "osgi-bundlecontext";
|
||||
public static final String OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME = "osgi-bundlecontext";
|
||||
|
||||
public static final String RACING_EVENT_SERVICE_TRACKER_NAME = "racingEventServiceTracker";
|
||||
|
||||
|
||||
+206
-11
@@ -5,6 +5,7 @@ import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -25,6 +26,7 @@ import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.xml.ws.http.HTTPException;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
@@ -33,6 +35,9 @@ import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.DefinedMarkFinder;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogCloseOpenEndedDeviceMappingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
|
||||
@@ -41,32 +46,50 @@ import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitors
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Nationality;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl;
|
||||
import com.sap.sailing.domain.regattalike.HasRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.IsRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.FlatGPSFixJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.FlatGPSFixJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.MarkJsonSerializerWithPosition;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
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;
|
||||
|
||||
@Path("/v1/leaderboards")
|
||||
@@ -368,9 +391,9 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
String deviceUuid = (String) requestObject.get(DeviceMappingConstants.JSON_DEVICE_UUID);
|
||||
Long fromMillis = (Long) requestObject.get(DeviceMappingConstants.JSON_FROM_MILLIS);
|
||||
|
||||
//don't need the device type and push ID yet - important once we start add support for push notifications
|
||||
//String deviceType = (String) requestObject.get(DeviceMappingConstants.JSON_DEVICE_TYPE);
|
||||
//String pushDeviceId = (String) requestObject.get(DeviceMappingConstants.JSON_PUSH_DEVICE_ID);
|
||||
// don't need the device type and push ID yet - important once we start add support for push notifications
|
||||
// String deviceType = (String) requestObject.get(DeviceMappingConstants.JSON_DEVICE_TYPE);
|
||||
// String pushDeviceId = (String) requestObject.get(DeviceMappingConstants.JSON_PUSH_DEVICE_ID);
|
||||
|
||||
if (competitorId == null || deviceUuid == null || fromMillis == null) {
|
||||
// || deviceType == null
|
||||
@@ -385,7 +408,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
return Response.status(Status.BAD_REQUEST).entity("No competitor found for id " + competitorId)
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
//add registration if necessary
|
||||
// add registration if necessary
|
||||
Set<Competitor> registered = new RegisteredCompetitorsAnalyzer<>(isRegattaLike.getRegattaLog()).analyze();
|
||||
if (!registered.contains(mappedTo)) {
|
||||
isRegattaLike.getRegattaLog().add(
|
||||
@@ -393,7 +416,8 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
}
|
||||
DeviceIdentifier device = new SmartphoneUUIDIdentifierImpl(UUID.fromString(deviceUuid));
|
||||
TimePoint from = new MillisecondsTimePoint(fromMillis);
|
||||
event = new RegattaLogDeviceCompetitorMappingEventImpl(now, author, now, UUID.randomUUID(), mappedTo, device, from, null);
|
||||
event = new RegattaLogDeviceCompetitorMappingEventImpl(now, author, now, UUID.randomUUID(), mappedTo, device,
|
||||
from, null);
|
||||
isRegattaLike.getRegattaLog().add(event);
|
||||
logger.fine("Successfully checked in competitor " + mappedTo.getName());
|
||||
return Response.status(Status.OK).build();
|
||||
@@ -451,7 +475,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
String deviceUuid = (String) requestObject.get("deviceUuid");
|
||||
TimePoint closingTimePoint = new MillisecondsTimePoint(toMillis);
|
||||
|
||||
if (toMillis == null || deviceUuid == null || closingTimePoint == null){
|
||||
if (toMillis == null || deviceUuid == null || closingTimePoint == null) {
|
||||
logger.warning("Invalid JSON body in request");
|
||||
return Response.status(Status.BAD_REQUEST).entity("Invalid JSON body in request")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
@@ -464,8 +488,8 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
OpenEndedDeviceMappingFinder finder = new OpenEndedDeviceMappingFinder(isRegattaLike.getRegattaLog(),
|
||||
mappedTo, deviceUuid);
|
||||
OpenEndedDeviceMappingFinder finder = new OpenEndedDeviceMappingFinder(isRegattaLike.getRegattaLog(), mappedTo,
|
||||
deviceUuid);
|
||||
Serializable deviceMappingEventId = finder.analyze();
|
||||
|
||||
if (deviceMappingEventId == null) {
|
||||
@@ -486,7 +510,8 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{leaderboardName}/competitors/{competitorId}")
|
||||
public Response getCompetitor(@PathParam("leaderboardName") String leaderboardName, @PathParam("competitorId") String competitorIdAsString) {
|
||||
public Response getCompetitor(@PathParam("leaderboardName") String leaderboardName,
|
||||
@PathParam("competitorId") String competitorIdAsString) {
|
||||
Response response;
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
Competitor competitor = getService().getCompetitorStore().getExistingCompetitorByIdAsString(
|
||||
@@ -494,8 +519,8 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
|
||||
if (competitor == null) {
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a competitor with id '" + competitorIdAsString + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
.entity("Could not find a competitor with id '" + competitorIdAsString + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
} else if (leaderboard == null) {
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a leaderboard with name '" + leaderboardName + "'.")
|
||||
@@ -507,4 +532,174 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("{leaderboardName}/marks")
|
||||
public Response getMarksForRace(@PathParam("leaderboardName") String leaderboardName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME) String raceColumnName,
|
||||
@QueryParam(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME) String fleetName) {
|
||||
// TODO also look for defined marks in RegattaLog?
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard == null) {
|
||||
return Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a leaderboard with name '" + leaderboardName + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
final Set<Mark> marks;
|
||||
if (raceColumnName == null) {
|
||||
if (fleetName != null) {
|
||||
return Response
|
||||
.status(Status.BAD_REQUEST)
|
||||
.entity("Either specify neither raceColumnName nor fleetName, only raceColumnName, or raceColumnName and fleetName but not only fleetName")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
} else {
|
||||
marks = new HashSet<Mark>();
|
||||
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
|
||||
for (Fleet fleet : raceColumn.getFleets()) {
|
||||
RaceLog raceLog = raceColumn.getRaceLog(fleet);
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); // might not yet be attached
|
||||
Util.addAll(new DefinedMarkFinder(raceLog).analyze(), marks);
|
||||
if (trackedRace != null) {
|
||||
Util.addAll(trackedRace.getMarks(), marks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName);
|
||||
if (raceColumn == null) {
|
||||
return Response
|
||||
.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race column '" + raceColumnName + "' in leaderboard '"
|
||||
+ leaderboardName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
} else if (fleetName != null) {
|
||||
Fleet fleet = raceColumn.getFleetByName(fleetName);
|
||||
if (fleet == null) {
|
||||
return Response
|
||||
.status(Status.NOT_FOUND)
|
||||
.entity("Could not find fleet '" + fleetName + "' in leaderboard '" + leaderboardName
|
||||
+ "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
} else {
|
||||
marks = getMarksForFleet(raceColumn, fleet);
|
||||
}
|
||||
} else {
|
||||
// Return all marks for a certain race column
|
||||
marks = new HashSet<Mark>();
|
||||
for (Fleet fleet : raceColumn.getFleets()) {
|
||||
Util.addAll(getMarksForFleet(raceColumn, fleet), marks);
|
||||
}
|
||||
}
|
||||
}
|
||||
JSONArray array = new JSONArray();
|
||||
for (Mark mark : marks) {
|
||||
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
|
||||
Position lastKnownPosition = getService().getMarkPosition(mark,
|
||||
(LeaderboardThatHasRegattaLike) leaderboard, now,
|
||||
/* raceLog==null means use all race logs */ null);
|
||||
array.add(markWithPositionSerializer.serialize(new Pair<>(mark, lastKnownPosition)));
|
||||
}
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("marks", array);
|
||||
return Response.ok(result.toJSONString(), MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
|
||||
private Set<Mark> getMarksForFleet(RaceColumn raceColumn, Fleet fleet) {
|
||||
RaceLog raceLog = raceColumn.getRaceLog(fleet);
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); // might not yet be attached
|
||||
Set<Mark> marks = new HashSet<>();
|
||||
Util.addAll(new DefinedMarkFinder(raceLog).analyze(), marks);
|
||||
if (trackedRace != null) {
|
||||
Util.addAll(trackedRace.getMarks(), marks);
|
||||
}
|
||||
return marks;
|
||||
}
|
||||
|
||||
private final MarkJsonSerializer markSerializer = new MarkJsonSerializer();
|
||||
private final MarkJsonSerializerWithPosition markWithPositionSerializer = new MarkJsonSerializerWithPosition(markSerializer, new FlatGPSFixJsonSerializer());
|
||||
private final FlatGPSFixJsonDeserializer fixDeserializer = new FlatGPSFixJsonDeserializer();
|
||||
private final FlatGPSFixJsonSerializer fixSerializer = new FlatGPSFixJsonSerializer();
|
||||
|
||||
/**
|
||||
* Mockito requires this to be public in order to be able to mock it :-(
|
||||
*/
|
||||
public RaceLogTrackingAdapter getRaceLogTrackingAdapter() {
|
||||
return getService(RaceLogTrackingAdapterFactory.class).getAdapter(getService().getBaseDomainFactory());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{leaderboardName}/marks/{markId}/gps_fixes")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
/**
|
||||
* Add the fixes to the GPSFixStore and create mappings for each fix in the RegattaLog.
|
||||
* @param json
|
||||
* @param leaderboardName
|
||||
* @param markId
|
||||
* @return
|
||||
* @throws HTTPException
|
||||
*/
|
||||
public Response pingMark(String json, @PathParam("leaderboardName") String leaderboardName,
|
||||
@PathParam("markId") String markId) throws HTTPException {
|
||||
logger.fine("Post issued to " + this.getClass().getName());
|
||||
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard == null) {
|
||||
return Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a leaderboard with name '" + leaderboardName + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
RegattaLog regattaLog = null;
|
||||
if (leaderboard instanceof HasRegattaLike) {
|
||||
regattaLog = ((HasRegattaLike) leaderboard).getRegattaLike().getRegattaLog();
|
||||
} else {
|
||||
return Response.status(Status.BAD_REQUEST)
|
||||
.entity("Leaderboard '" + leaderboardName + "' does not have an attached RegattaLog.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
Mark mark = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markId);
|
||||
if (mark == null) {
|
||||
return Response.status(Status.NOT_FOUND).entity("Could not find a mark with ID '" + markId + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
MillisecondsTimePoint now = MillisecondsTimePoint.now();
|
||||
Position lastKnownPosition = getService().getMarkPosition(mark,
|
||||
(LeaderboardThatHasRegattaLike) leaderboard, now,
|
||||
/* raceLog==null means use all race logs */ null);
|
||||
GPSFix fix = null;
|
||||
try {
|
||||
Object requestBody = JSONValue.parseWithException(json);
|
||||
JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody);
|
||||
logger.fine("JSON requestObject is: " + requestObject.toString());
|
||||
fix = fixDeserializer.deserialize(requestObject);
|
||||
} catch (ParseException | JsonDeserializationException | NumberFormatException e) {
|
||||
logger.warning(String.format("Exception while parsing post request:\n%s", e.toString()));
|
||||
return Response.status(Status.BAD_REQUEST).entity("Invalid JSON body in request")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
RaceLogTrackingAdapter adapter = getRaceLogTrackingAdapter();
|
||||
RacingEventService service = getService();
|
||||
try {
|
||||
if (lastKnownPosition != null) {
|
||||
// ping again to avoid interpolation, avoid filtering by paying attention to maxSpeedForSmoothing
|
||||
final Distance distance = lastKnownPosition.getDistance(fix.getPosition());
|
||||
final Duration minDuration = GPSFixTrackImpl.getDefaultMaxSpeedForSmoothing().getDuration(distance);
|
||||
final GPSFix repeatedFixAvoidingCreepingMark = new GPSFixImpl(lastKnownPosition, fix.getTimePoint().minus(minDuration));
|
||||
adapter.pingMark(regattaLog, mark, repeatedFixAvoidingCreepingMark, service);
|
||||
}
|
||||
adapter.pingMark(regattaLog, mark, fix, service);
|
||||
logger.log(Level.INFO, "Pinged mark " + mark.getName());
|
||||
} catch (NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not ping mark " + mark.getName());
|
||||
}
|
||||
|
||||
if (lastKnownPosition != null) {
|
||||
JSONObject lastKnownFixJson = fixSerializer.serialize(new GPSFixImpl(lastKnownPosition, now));
|
||||
String fixJson = lastKnownFixJson.toJSONString();
|
||||
return Response.ok(fixJson, MediaType.APPLICATION_JSON).build();
|
||||
} else {
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
+207
-109
@@ -70,11 +70,14 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
private static final Logger logger = Logger.getLogger(RegattasResource.class.getName());
|
||||
|
||||
private Response getBadRegattaErrorResponse(String regattaName) {
|
||||
return Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
private Response getBadRaceErrorResponse(String regattaName, String raceName) {
|
||||
return Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "' in regatta '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "' in regatta '" + regattaName + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -99,7 +102,8 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
if (regatta == null) {
|
||||
response = getBadRegattaErrorResponse(regattaName);
|
||||
} else {
|
||||
SeriesJsonSerializer seriesJsonSerializer = new SeriesJsonSerializer(new FleetJsonSerializer(new ColorJsonSerializer()));
|
||||
SeriesJsonSerializer seriesJsonSerializer = new SeriesJsonSerializer(new FleetJsonSerializer(
|
||||
new ColorJsonSerializer()));
|
||||
JsonSerializer<Regatta> regattaSerializer = new RegattaJsonSerializer(seriesJsonSerializer, null);
|
||||
JSONObject serializedRegatta = regattaSerializer.serialize(regatta);
|
||||
|
||||
@@ -111,7 +115,9 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
|
||||
/**
|
||||
* Gets all entries for a regatta.
|
||||
* @param regattaName the name of the regatta
|
||||
*
|
||||
* @param regattaName
|
||||
* the name of the regatta
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@@ -150,9 +156,10 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/races/{racename}/competitors/positions")
|
||||
public Response getCompetitorPositions(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@QueryParam("fromtime") String fromtime, @QueryParam("fromtimeasmillis") Long fromtimeasmillis,
|
||||
@QueryParam("totime") String totime, @QueryParam("totimeasmillis") Long totimeasmillis, @QueryParam("withtack") Boolean withTack,
|
||||
public Response getCompetitorPositions(@PathParam("regattaname") String regattaName,
|
||||
@PathParam("racename") String raceName, @QueryParam("fromtime") String fromtime,
|
||||
@QueryParam("fromtimeasmillis") Long fromtimeasmillis, @QueryParam("totime") String totime,
|
||||
@QueryParam("totimeasmillis") Long totimeasmillis, @QueryParam("withtack") Boolean withTack,
|
||||
@QueryParam("competitorId") Set<String> competitorIds) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
@@ -170,14 +177,17 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
try {
|
||||
from = parseTimePoint(fromtime, fromtimeasmillis,
|
||||
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
|
||||
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis()-24*3600*1000));
|
||||
/* 24h before race start */new MillisecondsTimePoint(trackedRace.getStartOfRace()
|
||||
.asMillis() - 24 * 3600 * 1000));
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
try {
|
||||
to = parseTimePoint(totime, totimeasmillis, MillisecondsTimePoint.now());
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
JSONObject jsonRace = new JSONObject();
|
||||
@@ -185,12 +195,14 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
jsonRace.put("regatta", regatta.getName());
|
||||
JSONArray jsonCompetitors = new JSONArray();
|
||||
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
if (competitorIds == null || competitorIds.isEmpty() || competitorIds.contains(competitor.getId().toString())) {
|
||||
if (competitorIds == null || competitorIds.isEmpty()
|
||||
|| competitorIds.contains(competitor.getId().toString())) {
|
||||
JSONObject jsonCompetitor = new JSONObject();
|
||||
jsonCompetitor.put("id", competitor.getId() != null ? competitor.getId().toString() : null);
|
||||
jsonCompetitor.put("name", competitor.getName());
|
||||
jsonCompetitor.put("sailNumber", competitor.getBoat().getSailID());
|
||||
jsonCompetitor.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml() : null);
|
||||
jsonCompetitor.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml()
|
||||
: null);
|
||||
GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
|
||||
JSONArray jsonFixes = new JSONArray();
|
||||
track.lockForRead();
|
||||
@@ -247,24 +259,29 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
|
||||
/**
|
||||
* Gets all GPS positions of the course marks for a given race.
|
||||
* @param regattaName the name of the regatta
|
||||
*
|
||||
* @param regattaName
|
||||
* the name of the regatta
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/races/{racename}/marks/positions")
|
||||
public Response getMarkPositions(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@QueryParam("fromtime") String fromtime, @QueryParam("fromtimeasmillis") Long fromtimeasmillis,
|
||||
@QueryParam("totime") String totime, @QueryParam("totimeasmillis") Long totimeasmillis) {
|
||||
Response response;
|
||||
public Response getMarkPositions(@PathParam("regattaname") String regattaName,
|
||||
@PathParam("racename") String raceName, @QueryParam("fromtime") String fromtime,
|
||||
@QueryParam("fromtimeasmillis") Long fromtimeasmillis, @QueryParam("totime") String totime,
|
||||
@QueryParam("totimeasmillis") Long totimeasmillis,
|
||||
@DefaultValue("false") @QueryParam("lastknown") boolean addLastKnown) {
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = getBadRegattaErrorResponse(regattaName);
|
||||
} else {
|
||||
return getBadRegattaErrorResponse(regattaName);
|
||||
}
|
||||
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = getBadRaceErrorResponse(regattaName, raceName);
|
||||
} else {
|
||||
return getBadRaceErrorResponse(regattaName, raceName);
|
||||
}
|
||||
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
|
||||
TimePoint from;
|
||||
@@ -272,14 +289,17 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
try {
|
||||
from = parseTimePoint(fromtime, fromtimeasmillis,
|
||||
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
|
||||
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis()-24*3600*1000));
|
||||
/* 24h before race start */new MillisecondsTimePoint(
|
||||
trackedRace.getStartOfRace().asMillis() - 24 * 3600 * 1000));
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
try {
|
||||
to = parseTimePoint(totime, totimeasmillis, MillisecondsTimePoint.now());
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
JSONObject jsonRace = new JSONObject();
|
||||
@@ -308,17 +328,24 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
} else {
|
||||
fixIter = track.getFixesIterator(from, /* inclusive */true);
|
||||
}
|
||||
GPSFix fix = null;
|
||||
boolean lastAdded = false;
|
||||
while (fixIter.hasNext()) {
|
||||
GPSFix fix = fixIter.next();
|
||||
fix = fixIter.next();
|
||||
if (to != null && fix.getTimePoint() != null && to.compareTo(fix.getTimePoint()) < 0) {
|
||||
lastAdded = false;
|
||||
break;
|
||||
}
|
||||
JSONObject jsonFix = new JSONObject();
|
||||
jsonFix.put("timepoint-ms", fix.getTimePoint().asMillis());
|
||||
jsonFix.put("lat-deg", UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLatDeg()));
|
||||
jsonFix.put("lng-deg", UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLngDeg()));
|
||||
JSONObject jsonFix = constructFixJson(fix);
|
||||
jsonFixes.add(jsonFix);
|
||||
lastAdded = true;
|
||||
}
|
||||
|
||||
if (addLastKnown && fix != null && !lastAdded){
|
||||
JSONObject jsonFix = constructFixJson(fix);
|
||||
jsonFixes.add(jsonFix);
|
||||
}
|
||||
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
@@ -328,15 +355,25 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
jsonRace.put("marks", jsonMarks);
|
||||
|
||||
String json = jsonRace.toJSONString();
|
||||
response = Response.ok(json, MediaType.APPLICATION_JSON).build();
|
||||
|
||||
return Response.ok(json, MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
|
||||
private JSONObject constructFixJson(GPSFix fix) {
|
||||
JSONObject jsonFix = new JSONObject();
|
||||
jsonFix.put("timepoint-ms", fix.getTimePoint().asMillis());
|
||||
jsonFix.put("lat-deg",
|
||||
UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLatDeg()));
|
||||
jsonFix.put("lng-deg",
|
||||
UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLngDeg()));
|
||||
return jsonFix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the course of the race.
|
||||
* @param regattaName the name of the regatta
|
||||
*
|
||||
* @param regattaName
|
||||
* the name of the regatta
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@@ -353,11 +390,9 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
response = getBadRaceErrorResponse(regattaName, raceName);
|
||||
} else {
|
||||
Course course = race.getCourse();
|
||||
CourseBaseJsonSerializer serializer = new CourseBaseJsonSerializer(
|
||||
new WaypointJsonSerializer(
|
||||
new ControlPointJsonSerializer(
|
||||
new MarkJsonSerializer(),
|
||||
new GateJsonSerializer(new MarkJsonSerializer()))));
|
||||
CourseBaseJsonSerializer serializer = new CourseBaseJsonSerializer(new WaypointJsonSerializer(
|
||||
new ControlPointJsonSerializer(new MarkJsonSerializer(), new GateJsonSerializer(
|
||||
new MarkJsonSerializer()))));
|
||||
|
||||
JSONObject jsonCourse = serializer.serialize(course);
|
||||
String json = jsonCourse.toJSONString();
|
||||
@@ -369,7 +404,9 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
|
||||
/**
|
||||
* Gets the relevant times of the race.
|
||||
* @param regattaName the name of the regatta
|
||||
*
|
||||
* @param regattaName
|
||||
* the name of the regatta
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@@ -391,16 +428,22 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
jsonRaceTimes.put("name", trackedRace.getRace().getName());
|
||||
jsonRaceTimes.put("regatta", regatta.getName());
|
||||
|
||||
jsonRaceTimes.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asMillis());
|
||||
jsonRaceTimes.put("startOfTracking-ms", trackedRace.getStartOfTracking() == null ? null : trackedRace.getStartOfTracking().asMillis());
|
||||
jsonRaceTimes.put("newestTrackingEvent-ms", trackedRace.getTimePointOfNewestEvent() == null ? null : trackedRace.getTimePointOfNewestEvent().asMillis());
|
||||
jsonRaceTimes.put("endOfTracking-ms", trackedRace.getEndOfTracking() == null ? null : trackedRace.getEndOfTracking().asMillis());
|
||||
jsonRaceTimes.put("endOfRace-ms", trackedRace.getEndOfRace() == null ? null : trackedRace.getEndOfRace().asMillis());
|
||||
jsonRaceTimes.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace
|
||||
.getStartOfRace().asMillis());
|
||||
jsonRaceTimes.put("startOfTracking-ms", trackedRace.getStartOfTracking() == null ? null : trackedRace
|
||||
.getStartOfTracking().asMillis());
|
||||
jsonRaceTimes.put("newestTrackingEvent-ms", trackedRace.getTimePointOfNewestEvent() == null ? null
|
||||
: trackedRace.getTimePointOfNewestEvent().asMillis());
|
||||
jsonRaceTimes.put("endOfTracking-ms", trackedRace.getEndOfTracking() == null ? null : trackedRace
|
||||
.getEndOfTracking().asMillis());
|
||||
jsonRaceTimes.put("endOfRace-ms", trackedRace.getEndOfRace() == null ? null : trackedRace
|
||||
.getEndOfRace().asMillis());
|
||||
jsonRaceTimes.put("delayToLive-ms", trackedRace.getDelayToLiveInMillis());
|
||||
|
||||
JSONArray jsonMarkPassingTimes = new JSONArray();
|
||||
List<TimePoint> firstPassingTimepoints = new ArrayList<>();
|
||||
Iterable<com.sap.sse.common.Util.Pair<Waypoint, com.sap.sse.common.Util.Pair<TimePoint, TimePoint>>> markPassingsTimes = trackedRace.getMarkPassingsTimes();
|
||||
Iterable<com.sap.sse.common.Util.Pair<Waypoint, com.sap.sse.common.Util.Pair<TimePoint, TimePoint>>> markPassingsTimes = trackedRace
|
||||
.getMarkPassingsTimes();
|
||||
synchronized (markPassingsTimes) {
|
||||
int numberOfWaypoints = Util.size(markPassingsTimes);
|
||||
int wayPointNumber = 1;
|
||||
@@ -414,8 +457,10 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
com.sap.sse.common.Util.Pair<TimePoint, TimePoint> timesPair = markPassingTimes.getB();
|
||||
TimePoint firstPassingTime = timesPair.getA();
|
||||
TimePoint lastPassingTime = timesPair.getB();
|
||||
jsonMarkPassing.put("firstPassing-ms", firstPassingTime == null ? null : firstPassingTime.asMillis());
|
||||
jsonMarkPassing.put("lastPassing-ms", lastPassingTime == null ? null : lastPassingTime.asMillis());
|
||||
jsonMarkPassing.put("firstPassing-ms",
|
||||
firstPassingTime == null ? null : firstPassingTime.asMillis());
|
||||
jsonMarkPassing.put("lastPassing-ms",
|
||||
lastPassingTime == null ? null : lastPassingTime.asMillis());
|
||||
|
||||
firstPassingTimepoints.add(firstPassingTime);
|
||||
|
||||
@@ -438,7 +483,10 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
TimePoint firstPassingTime = firstPassingTimepoints.get(legNumber - 1);
|
||||
if (firstPassingTime != null) {
|
||||
jsonLegInfo.put("type", trackedLeg.getLegType(firstPassingTime));
|
||||
jsonLegInfo.put("bearing-deg", UnitSerializationUtil.bearingDecimalFormatter.format(trackedLeg.getLegBearing(firstPassingTime).getDegrees()));
|
||||
jsonLegInfo.put(
|
||||
"bearing-deg",
|
||||
UnitSerializationUtil.bearingDecimalFormatter.format(trackedLeg.getLegBearing(
|
||||
firstPassingTime).getDegrees()));
|
||||
}
|
||||
} catch (NoWindException e) {
|
||||
// do nothing
|
||||
@@ -469,11 +517,15 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
JSONArray windSourcesAvailable = new JSONArray();
|
||||
@@ -496,17 +548,21 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
@Path("{regattaname}/races/{racename}/wind")
|
||||
public Response getWind(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@DefaultValue("COMBINED") @QueryParam("windsource") String windSource,
|
||||
@QueryParam("windsourceid") String windSourceId,
|
||||
@QueryParam("fromtime") String fromtime, @QueryParam("fromtimeasmillis") Long fromtimeasmillis,
|
||||
@QueryParam("totime") String totime, @QueryParam("totimeasmillis") Long totimeasmillis) {
|
||||
@QueryParam("windsourceid") String windSourceId, @QueryParam("fromtime") String fromtime,
|
||||
@QueryParam("fromtimeasmillis") Long fromtimeasmillis, @QueryParam("totime") String totime,
|
||||
@QueryParam("totimeasmillis") Long totimeasmillis) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
|
||||
@@ -515,17 +571,21 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
try {
|
||||
from = parseTimePoint(fromtime, fromtimeasmillis,
|
||||
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
|
||||
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis()-24*3600*1000));
|
||||
/* 24h before race start */new MillisecondsTimePoint(trackedRace.getStartOfRace()
|
||||
.asMillis() - 24 * 3600 * 1000));
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
try {
|
||||
to = parseTimePoint(totime, totimeasmillis, MillisecondsTimePoint.now());
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
TrackedRaceJsonSerializer serializer = new TrackedRaceJsonSerializer(new DefaultWindTrackJsonSerializer());
|
||||
TrackedRaceJsonSerializer serializer = new TrackedRaceJsonSerializer(
|
||||
new DefaultWindTrackJsonSerializer());
|
||||
serializer.setWindSource(windSource);
|
||||
serializer.setWindSourceId(windSourceId);
|
||||
serializer.setFromTime(from);
|
||||
@@ -542,29 +602,38 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/races/{racename}/firstlegbearing")
|
||||
public Response getFirstLegBearing(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@QueryParam("time") String time, @QueryParam("timeasmillis") Long timeasmillis) {
|
||||
public Response getFirstLegBearing(@PathParam("regattaname") String regattaName,
|
||||
@PathParam("racename") String raceName, @QueryParam("time") String time,
|
||||
@QueryParam("timeasmillis") Long timeasmillis) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
final TimePoint timePoint;
|
||||
try {
|
||||
timePoint = parseTimePoint(time, timeasmillis,
|
||||
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
|
||||
trackedRace.getStartOfRace());
|
||||
timePoint = parseTimePoint(
|
||||
time,
|
||||
timeasmillis,
|
||||
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) : trackedRace
|
||||
.getStartOfRace());
|
||||
} catch (InvalidDateException e1) {
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
|
||||
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
BearingJsonSerializer serializer = new BearingJsonSerializer();
|
||||
JSONObject jsonBearing = serializer.serialize(trackedRace.getDirectionFromStartToNextMark(timePoint).getFrom());
|
||||
JSONObject jsonBearing = serializer.serialize(trackedRace.getDirectionFromStartToNextMark(timePoint)
|
||||
.getFrom());
|
||||
String json = jsonBearing.toJSONString();
|
||||
return Response.ok(json, MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
@@ -579,11 +648,15 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
MarkPassingsJsonSerializer serializer = new MarkPassingsJsonSerializer();
|
||||
@@ -625,15 +698,20 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/races/{racename}/competitors/legs")
|
||||
public Response getCompetitorRanks(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) {
|
||||
public Response getCompetitorRanks(@PathParam("regattaname") String regattaName,
|
||||
@PathParam("racename") String raceName) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
TimePoint timePoint = trackedRace.getTimePointOfNewestEvent() == null ? MillisecondsTimePoint.now()
|
||||
@@ -642,7 +720,8 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
JSONObject jsonRaceResults = new JSONObject();
|
||||
jsonRaceResults.put("name", trackedRace.getRace().getName());
|
||||
jsonRaceResults.put("regatta", regatta.getName());
|
||||
jsonRaceResults.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asMillis());
|
||||
jsonRaceResults.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace
|
||||
.getStartOfRace().asMillis());
|
||||
|
||||
JSONArray jsonLegs = new JSONArray();
|
||||
for (TrackedLeg leg : trackedRace.getTrackedLegs()) {
|
||||
@@ -663,20 +742,25 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
JSONObject jsonCompetitorInLeg = new JSONObject();
|
||||
TrackedLegOfCompetitor trackedLegOfCompetitor = leg.getTrackedLeg(competitor);
|
||||
if (trackedLegOfCompetitor != null) {
|
||||
jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId().toString() : null);
|
||||
jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId().toString()
|
||||
: null);
|
||||
jsonCompetitorInLeg.put("name", competitor.getName());
|
||||
jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID());
|
||||
jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml() : null);
|
||||
jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor()
|
||||
.getAsHtml() : null);
|
||||
|
||||
Speed averageSpeedOverGround = trackedLegOfCompetitor.getAverageSpeedOverGround(timePoint);
|
||||
if(averageSpeedOverGround != null) {
|
||||
jsonCompetitorInLeg.put("averageSOG-kts", UnitSerializationUtil.knotsDecimalFormatter.format(averageSpeedOverGround.getKnots()));
|
||||
if (averageSpeedOverGround != null) {
|
||||
jsonCompetitorInLeg.put("averageSOG-kts", UnitSerializationUtil.knotsDecimalFormatter
|
||||
.format(averageSpeedOverGround.getKnots()));
|
||||
}
|
||||
try {
|
||||
Integer numberOfTacks = trackedLegOfCompetitor.getNumberOfTacks(timePoint, /* waitForLatest */ false);
|
||||
Integer numberOfJibes = trackedLegOfCompetitor.getNumberOfJibes(timePoint, /* waitForLatest */ false);
|
||||
Integer numberOfPenaltyCircles = trackedLegOfCompetitor
|
||||
.getNumberOfPenaltyCircles(timePoint, /* waitForLatest */ false);
|
||||
Integer numberOfTacks = trackedLegOfCompetitor.getNumberOfTacks(timePoint, /* waitForLatest */
|
||||
false);
|
||||
Integer numberOfJibes = trackedLegOfCompetitor.getNumberOfJibes(timePoint, /* waitForLatest */
|
||||
false);
|
||||
Integer numberOfPenaltyCircles = trackedLegOfCompetitor.getNumberOfPenaltyCircles(
|
||||
timePoint, /* waitForLatest */false);
|
||||
jsonCompetitorInLeg.put("tacks", numberOfTacks);
|
||||
jsonCompetitorInLeg.put("jibes", numberOfJibes);
|
||||
jsonCompetitorInLeg.put("penaltyCircles", numberOfPenaltyCircles);
|
||||
@@ -701,8 +785,8 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
if (timeSinceGun > 0) {
|
||||
jsonCompetitorInLeg.put("timeSinceGun-ms", timeSinceGun);
|
||||
}
|
||||
Distance distanceSinceGun = trackedRace.getTrack(competitor).getDistanceTraveled(startOfRace,
|
||||
finishTime != null ? finishTime : timePoint);
|
||||
Distance distanceSinceGun = trackedRace.getTrack(competitor).getDistanceTraveled(
|
||||
startOfRace, finishTime != null ? finishTime : timePoint);
|
||||
if (distanceSinceGun != null) {
|
||||
jsonCompetitorInLeg.put("distanceSinceGun-m", distanceSinceGun.getMeters());
|
||||
}
|
||||
@@ -710,7 +794,9 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
|
||||
Distance distanceTraveled = trackedLegOfCompetitor.getDistanceTraveled(timePoint);
|
||||
if (distanceTraveled != null) {
|
||||
jsonCompetitorInLeg.put("distanceTraveled-m", UnitSerializationUtil.distanceDecimalFormatter.format(distanceTraveled.getMeters()));
|
||||
jsonCompetitorInLeg.put("distanceTraveled-m",
|
||||
UnitSerializationUtil.distanceDecimalFormatter.format(distanceTraveled
|
||||
.getMeters()));
|
||||
}
|
||||
Distance distanceTraveledIncludingGateStart = trackedLegOfCompetitor.getDistanceTraveledConsideringGateStart(timePoint);
|
||||
if (distanceTraveledIncludingGateStart != null) {
|
||||
@@ -729,8 +815,8 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
}
|
||||
}
|
||||
try {
|
||||
jsonCompetitorInLeg.put("gapToLeader-s",
|
||||
trackedLegOfCompetitor.getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE));
|
||||
jsonCompetitorInLeg.put("gapToLeader-s", trackedLegOfCompetitor
|
||||
.getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE));
|
||||
} catch (NoWindException e1) {
|
||||
// well, we don't know the wind direction... then no gap to leader will be shown...
|
||||
}
|
||||
@@ -754,16 +840,20 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/races/{racename}/competitors/live")
|
||||
public Response getCompetitorLiveRanks(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@DefaultValue("-1") @QueryParam("topN") Integer topN) {
|
||||
public Response getCompetitorLiveRanks(@PathParam("regattaname") String regattaName,
|
||||
@PathParam("racename") String raceName, @DefaultValue("-1") @QueryParam("topN") Integer topN) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
Course course = trackedRace.getRace().getCourse();
|
||||
@@ -777,12 +867,12 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
jsonLiveData.put("name", trackedRace.getRace().getName());
|
||||
jsonLiveData.put("regatta", regatta.getName());
|
||||
|
||||
if(trackedRace.getStartOfRace() != null) {
|
||||
if (trackedRace.getStartOfRace() != null) {
|
||||
TimePoint startOfRace = trackedRace.getStartOfRace();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
jsonLiveData.put("startTime", startOfRace.asMillis());
|
||||
jsonLiveData.put("liveTime", now.asMillis());
|
||||
if(startOfRace.before(now)) {
|
||||
if (startOfRace.before(now)) {
|
||||
jsonLiveData.put("timeSinceStart-s", (now.asMillis() - startOfRace.asMillis()) / 1000.0);
|
||||
} else {
|
||||
jsonLiveData.put("timeToStart-s", (startOfRace.asMillis() - now.asMillis()) / 1000.0);
|
||||
@@ -796,47 +886,56 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
for (Competitor competitor : competitorsFromBestToWorst) {
|
||||
JSONObject jsonCompetitorInLeg = new JSONObject();
|
||||
|
||||
if(topN != null && topN > 0 && rank > topN) {
|
||||
if (topN != null && topN > 0 && rank > topN) {
|
||||
break;
|
||||
}
|
||||
jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId().toString() : null);
|
||||
jsonCompetitorInLeg
|
||||
.put("id", competitor.getId() != null ? competitor.getId().toString() : null);
|
||||
jsonCompetitorInLeg.put("name", competitor.getName());
|
||||
jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID());
|
||||
jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml() : null);
|
||||
jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor()
|
||||
.getAsHtml() : null);
|
||||
jsonCompetitorInLeg.put("rank", rank++);
|
||||
|
||||
TrackedLegOfCompetitor currentLegOfCompetitor = trackedRace.getCurrentLeg(competitor, timePoint);
|
||||
TrackedLegOfCompetitor currentLegOfCompetitor = trackedRace
|
||||
.getCurrentLeg(competitor, timePoint);
|
||||
if (currentLegOfCompetitor != null) {
|
||||
int indexOfWaypoint = course.getIndexOfWaypoint(currentLegOfCompetitor.getLeg().getFrom());
|
||||
jsonCompetitorInLeg.put("leg", indexOfWaypoint+1);
|
||||
jsonCompetitorInLeg.put("leg", indexOfWaypoint + 1);
|
||||
|
||||
Speed speedOverGround = currentLegOfCompetitor.getSpeedOverGround(timePoint);
|
||||
if(speedOverGround != null) {
|
||||
jsonCompetitorInLeg.put("speedOverGround-kts", roundDouble(speedOverGround.getKnots(), 2));
|
||||
if (speedOverGround != null) {
|
||||
jsonCompetitorInLeg.put("speedOverGround-kts",
|
||||
roundDouble(speedOverGround.getKnots(), 2));
|
||||
}
|
||||
|
||||
Distance distanceTraveled = currentLegOfCompetitor.getDistanceTraveled(timePoint);
|
||||
if (distanceTraveled != null) {
|
||||
jsonCompetitorInLeg.put("distanceTraveled-m", roundDouble(distanceTraveled.getMeters(), 2));
|
||||
jsonCompetitorInLeg.put("distanceTraveled-m",
|
||||
roundDouble(distanceTraveled.getMeters(), 2));
|
||||
}
|
||||
Distance distanceTraveledConsideringGateStart = currentLegOfCompetitor.getDistanceTraveledConsideringGateStart(timePoint);
|
||||
if (distanceTraveledConsideringGateStart != null) {
|
||||
jsonCompetitorInLeg.put("distanceTraveledConsideringGateStart-m", roundDouble(distanceTraveledConsideringGateStart.getMeters(), 2));
|
||||
}
|
||||
|
||||
Double gapToLeaderInSeconds = currentLegOfCompetitor.getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE);
|
||||
if(gapToLeaderInSeconds != null) {
|
||||
Double gapToLeaderInSeconds = currentLegOfCompetitor.getGapToLeaderInSeconds(timePoint,
|
||||
WindPositionMode.LEG_MIDDLE);
|
||||
if (gapToLeaderInSeconds != null) {
|
||||
jsonCompetitorInLeg.put("gapToLeader-s", roundDouble(gapToLeaderInSeconds, 2));
|
||||
}
|
||||
|
||||
Distance windwardDistanceToOverallLeader = currentLegOfCompetitor.getWindwardDistanceToOverallLeader(timePoint, WindPositionMode.LEG_MIDDLE);
|
||||
if(windwardDistanceToOverallLeader != null) {
|
||||
jsonCompetitorInLeg.put("gapToLeader-m", roundDouble(windwardDistanceToOverallLeader.getMeters(), 2));
|
||||
Distance windwardDistanceToOverallLeader = currentLegOfCompetitor
|
||||
.getWindwardDistanceToOverallLeader(timePoint, WindPositionMode.LEG_MIDDLE);
|
||||
if (windwardDistanceToOverallLeader != null) {
|
||||
jsonCompetitorInLeg.put("gapToLeader-m",
|
||||
roundDouble(windwardDistanceToOverallLeader.getMeters(), 2));
|
||||
}
|
||||
jsonCompetitorInLeg.put("finished", false);
|
||||
} else {
|
||||
// we need to distinguish between competitors which did not start and competitors which already finished
|
||||
if(trackedRace.getMarkPassing(competitor, lastWaypoint) != null) {
|
||||
// we need to distinguish between competitors which did not start and competitors which
|
||||
// already finished
|
||||
if (trackedRace.getMarkPassing(competitor, lastWaypoint) != null) {
|
||||
jsonCompetitorInLeg.put("finished", true);
|
||||
} else {
|
||||
jsonCompetitorInLeg.put("finished", false);
|
||||
@@ -856,4 +955,3 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-5
@@ -19,6 +19,10 @@
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Input format:</td>
|
||||
<td>binary (not multipart)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>JSON</td>
|
||||
@@ -41,18 +45,26 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST (Binary data, not multipart)</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request example:</td>
|
||||
<td><pre>curl -H "Content-Type:image/jpeg" --data-binary @{image-path} \
|
||||
http://sapsailing.com/sailingserver/api/v1/competitors/{competitor-id}/team/image</pre></td>
|
||||
<td>
|
||||
<pre><code>
|
||||
curl -H "Content-Type:image/jpeg" --data-binary @{image-path} \
|
||||
http://sapsailing.com/sailingserver/api/v1/competitors/{competitor-id}/team/image
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Response example:</td>
|
||||
<td><pre>{
|
||||
<td>
|
||||
<pre><code>
|
||||
{
|
||||
"teamImageUri" : "http://training.sapsailing.com/team_images/9871d3a2c554b27151cacf1422eec048.jpeg"
|
||||
}</pre></td>
|
||||
}
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
@@ -21,8 +21,8 @@ These can be mapped to a competitor or a mark via the DeviceMappings in the Race
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Post data input format:</td>
|
||||
<td>JSON</td>
|
||||
<td>Input format:</td>
|
||||
<td>JSON (application/json)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
@@ -42,8 +42,7 @@ These can be mapped to a competitor or a mark via the DeviceMappings in the Race
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>
|
||||
POST the following JSON data to <a href="http://www.sapsailing.com/sailingserver/api/v1/gps_fixes">http://www.sapsailing.com/sailingserver/api/v1/gps_fixes</a>
|
||||
<td>POST <a href="http://www.sapsailing.com/sailingserver/api/v1/gps_fixes">http://www.sapsailing.com/sailingserver/api/v1/gps_fixes</a>
|
||||
<pre><code>
|
||||
{
|
||||
"deviceUuid" : "af855a56-9726-4a9c-a77e-da955bd289bf",
|
||||
@@ -64,7 +63,8 @@ These can be mapped to a competitor or a mark via the DeviceMappings in the Race
|
||||
}
|
||||
]
|
||||
}
|
||||
</code></pre></td>
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
@@ -116,6 +116,14 @@ name to make sure you get to the event you're interested in.
|
||||
<td><a href="leaderboardCompetitorGetDoc.html">/api/v1/leaderboards/{name}/competitors/{id}</a></td>
|
||||
<td>Gets the details of a competitor in the context of a leaderboard</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="leaderboardRaceMarksGetDoc.html">/api/v1/leaderboards/{name}/marks?race_column={name}&fleet={name}</a></td>
|
||||
<td>Gets the marks of a race (from the RaceLog and if possible the TrackedRace)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="leaderboardMarkGpsFixesPostDoc.html">/api/v1/leaderboards/{name}/marks/{id}/gps_fixes</a></td>
|
||||
<td>Pings the location of a mark via the RegattaLog</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="gpsFixesPostDoc.html">/api/v1/gps_fixes</a></td>
|
||||
<td>Adds GPS-fixes that originated from a device with a unique identifier</td>
|
||||
|
||||
+12
-8
@@ -19,9 +19,13 @@
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Input format:</td>
|
||||
<td>JSON (application/json)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>HTTP 200 (no body)</td>
|
||||
<td>HTTP 200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
@@ -36,18 +40,18 @@
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory input:</td>
|
||||
<td>JSON</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><pre>{
|
||||
<td>Example:</td>
|
||||
<td>POST <a href=http://sapsailing.com/api/v1/leaderboards/{name}/device_mappings/start>http://sapsailing.com/api/v1/leaderboards/{name}/device_mappings/start</a>
|
||||
<pre><code>
|
||||
{
|
||||
"competitorId" : "af855a56-9726-4a9c-a77e-da955bd289bg",
|
||||
"deviceType" : "ios",
|
||||
"deviceUuid" : "af855a56-9726-4a9c-a77e-da955bd289bf",
|
||||
"pushDeviceId" : "0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78",
|
||||
"fromMillis" : 1415624564718
|
||||
}</pre></td>
|
||||
}
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
+12
-8
@@ -19,9 +19,13 @@
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Input format:</td>
|
||||
<td>JSON (application/json)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>HTTP 200 (no body)</td>
|
||||
<td>HTTP 200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
@@ -36,16 +40,16 @@
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory input:</td>
|
||||
<td>JSON</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><pre>{
|
||||
<td>Example:</td>
|
||||
<td>POST <a href=http://sapsailing.com/api/v1/leaderboards/{name}/device_mappings/end>http://sapsailing.com/api/v1/leaderboards/{name}/device_mappings/end</a>
|
||||
<pre><code>
|
||||
{
|
||||
"competitorId" : "af855a56-9726-4a9c-a77e-da955bd289bg",
|
||||
"deviceUuid" : "af855a56-9726-4a9c-a77e-da955bd289bf",
|
||||
"toMillis" : 1415624564719
|
||||
}</pre></td>
|
||||
}
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0 Beta</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0 Beta</h1>
|
||||
<h2>URL: /api/v1/leaderboards/{name}/marks/{id}/gps_fixes</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Adds an arbitrary number GPS-fixes that originated from a device with a unique identifier as fixes for the mark.
|
||||
This is done by adding the fixes to the GPSFixStore, and creating device-mappings in the RegattaLog attached to the Leaderboard.</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Post data input format:</td>
|
||||
<td>JSON</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>HTTP 200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>POST <a href="http://www.sapsailing.com/sailingserver/api/v1/leaderboards/{name}/marks/{id}/gps_fixes">http://www.sapsailing.com/sailingserver/api/v1/leaderboards/{name}/marks/{id}/gps_fixes</a>
|
||||
<pre><code>
|
||||
{
|
||||
"deviceUuid" : "af855a56-9726-4a9c-a77e-da955bd289bf",
|
||||
"fixes" : [
|
||||
{
|
||||
"timestamp" : 14144160080000,
|
||||
"latitude" : 54.325246,
|
||||
"longitude" : 10.148556,
|
||||
"speed" : 3.61,
|
||||
"course" : 258.11,
|
||||
},
|
||||
{
|
||||
"timestamp" : 14144168490000,
|
||||
"latitude" : 55.12456,
|
||||
"longitude" : 8.03456,
|
||||
"speed" : 5.1,
|
||||
"course" : 14.2,
|
||||
}
|
||||
]
|
||||
}
|
||||
</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0 Beta</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0 Beta</h1>
|
||||
<h2>URL: /api/v1/leaderboards/{name}/marks?race_column={name}&fleet={name}</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Gets the marks of a race (from the RaceLog and if possible the TrackedRace). If the "fleet"
|
||||
parameter is specified then so must the race_column parameter. If no fleet is specified,
|
||||
the marks for all fleets of the specified race column are returned. If no race column and
|
||||
no fleet are specified, all marks of all races in the leaderboard are returned.</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>race_column
|
||||
<br/>fleet</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/leaderboards/{name}/marks?race_column={name}&fleet={name}">http://www.sapsailing.com/sailingserver/api/v1/leaderboards/{name}/marks?race_column={name}&fleet={name}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -34,6 +34,8 @@
|
||||
<div>fromtimeasmillis</div>
|
||||
<div>totime (ISO format, such as 2014-02-14T10:12:03Z)</div>
|
||||
<div>totimeasmillis</div>
|
||||
<div>lastknown (boolean, false or true; defaults to false; true will return the last known fix even
|
||||
if there is no fix for the mark within the interval requested)</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.base.EventBase;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.LeaderboardSearchResult;
|
||||
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
@@ -35,6 +36,7 @@ import com.sap.sailing.domain.base.configuration.DeviceConfigurationIdentifier;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
|
||||
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
|
||||
import com.sap.sailing.domain.common.DataImportProgress;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RaceFetcher;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaFetcher;
|
||||
@@ -54,6 +56,7 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.RaceListener;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
@@ -124,6 +127,22 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
|
||||
*/
|
||||
Leaderboard getLeaderboardByName(String name);
|
||||
|
||||
/**
|
||||
* Looks at the mark tracks in the tracked races attached to the <code>leaderboard</code>. If it doesn't find a
|
||||
* track for the <code>mark</code> requested there which has fixes before and after <code>timePoint</code> (to
|
||||
* ensure that no track cropping has taken place, removing the fixes for the interesting time period), looks in the
|
||||
* leaderboard's regatta log and the specific <code>raceLog</code> (if provided) or all race logs attached to the
|
||||
* leaderboard (if not provided) for device mappings for the mark and tries to load fixes from the
|
||||
* {@link GPSFixStore}. The latter is only necessary if the mark isn't found in any tracked race with fixes
|
||||
* surrounding <code>timePoint</code> because should there be a tracked race in the leaderboard that has the mark
|
||||
* then it will also have received the fixes from the {@link GPSFixStore} through the regatta log mapping.
|
||||
* <p>
|
||||
*
|
||||
* @return the position obtained by interpolation but never extrapolation from the track identified as described
|
||||
* above
|
||||
*/
|
||||
Position getMarkPosition(Mark mark, LeaderboardThatHasRegattaLike leaderboard, TimePoint timePoint, RaceLog raceLog);
|
||||
|
||||
/**
|
||||
* Stops tracking all races of the regatta specified. This will also stop tracking wind for all races of this regatta.
|
||||
* See {@link #stopTrackingWind(Regatta, RaceDefinition)}. If there were multiple calls to
|
||||
@@ -581,4 +600,5 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
|
||||
FileStorageManagementService getFileStorageManagementService();
|
||||
|
||||
ClassLoader getCombinedMasterDataClassLoader();
|
||||
|
||||
}
|
||||
+84
@@ -13,8 +13,10 @@ import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -43,6 +45,7 @@ import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLog;
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
@@ -85,6 +88,7 @@ import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl;
|
||||
import com.sap.sailing.domain.common.DataImportProgress;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaName;
|
||||
@@ -125,11 +129,13 @@ import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.IsRegattaLike;
|
||||
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
|
||||
import com.sap.sailing.domain.regattalog.RegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.RaceHandle;
|
||||
@@ -144,6 +150,7 @@ import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTracker;
|
||||
import com.sap.sailing.domain.tracking.WindTrackerFactory;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory;
|
||||
import com.sap.sailing.polars.PolarDataService;
|
||||
@@ -970,6 +977,83 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
return leaderboardsByName.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getMarkPosition(Mark mark, LeaderboardThatHasRegattaLike leaderboard, TimePoint timePoint, RaceLog raceLog) {
|
||||
GPSFixTrack<Mark, GPSFix> track = null;
|
||||
// If no spanning track is found, the fix closest to the time point requested is used instead
|
||||
GPSFix nonSpanningFallback = null;
|
||||
for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
|
||||
GPSFixTrack<Mark, GPSFix> trackCandidate = trackedRace.getTrack(mark);
|
||||
if (trackCandidate != null) {
|
||||
if (spansTimePoint(trackCandidate, timePoint)) {
|
||||
track = trackCandidate;
|
||||
break;
|
||||
} else {
|
||||
nonSpanningFallback = improveTimewiseClosestFix(nonSpanningFallback, trackCandidate, timePoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (track == null) { // no spanning track found in any tracked race, or no tracked races found
|
||||
// try to load from store
|
||||
DynamicGPSFixTrackImpl<Mark> loadedTrack = new DynamicGPSFixTrackImpl<Mark>(mark, 0);
|
||||
track = loadedTrack;
|
||||
Set<AbstractLog<?, ?>> logs = new HashSet<>();
|
||||
logs.add(leaderboard.getRegattaLike().getRegattaLog());
|
||||
if (raceLog == null) { // no race log explicitly provided --> use all race logs
|
||||
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
|
||||
for (Fleet fleet : raceColumn.getFleets()) {
|
||||
logs.add(raceColumn.getRaceLog(fleet));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logs.add(raceLog);
|
||||
}
|
||||
for (AbstractLog<?, ?> log : logs) {
|
||||
try {
|
||||
getGPSFixStore().loadMarkTrack(loadedTrack, log, mark);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Couldn't load mark track for mark " + mark + " from log " + log, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Position result = track.getEstimatedPosition(timePoint, /* extrapolate */ false);
|
||||
if (result == null) {
|
||||
result = nonSpanningFallback == null ? null : nonSpanningFallback.getPosition();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private GPSFix improveTimewiseClosestFix(GPSFix nonSpanningFallback, GPSFixTrack<Mark, GPSFix> track, final TimePoint timePoint) {
|
||||
GPSFix lastAtOrBefore = track.getLastFixAtOrBefore(timePoint);
|
||||
GPSFix firstAtOrAfter = track.getFirstFixAtOrAfter(timePoint);
|
||||
// find the fix closes to timePoint, sorting null values to the end and fixes near timePoint to the beginning
|
||||
final List<GPSFix> list = Arrays.asList(nonSpanningFallback, lastAtOrBefore, firstAtOrAfter);
|
||||
list.sort(new Comparator<GPSFix>() {
|
||||
@Override
|
||||
public int compare(GPSFix o1, GPSFix o2) {
|
||||
final int result;
|
||||
if (o1 == null) {
|
||||
if (o2 == null) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = 1;
|
||||
}
|
||||
} else if (o2 == null) {
|
||||
result = -1;
|
||||
} else {
|
||||
result = new Long(Math.abs(o1.getTimePoint().until(timePoint).asMillis())).compareTo(
|
||||
Math.abs(o2.getTimePoint().until(timePoint).asMillis()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
private boolean spansTimePoint(GPSFixTrack<Mark, GPSFix> track, TimePoint timePoint) {
|
||||
return track.getLastFixAtOrBefore(timePoint) != null && track.getFirstFixAtOrAfter(timePoint) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Leaderboard> getLeaderboards() {
|
||||
return Collections.unmodifiableMap(new HashMap<String, Leaderboard>(leaderboardsByName));
|
||||
|
||||
+5
@@ -562,4 +562,9 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GPSFixTrack<Mark, GPSFix> getTrack(Mark mark) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user