mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-24 14:38:45 +00:00
Merge branch 'bug4044'
This commit is contained in:
+121
-3
@@ -4,6 +4,7 @@ import static com.sap.sse.common.Util.size;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -26,6 +27,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
|
||||
@@ -484,6 +486,12 @@ public class SensorFixStoreAndLoadTest {
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2.getMeters()));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3.getMeters()));
|
||||
}
|
||||
|
||||
private void addMoreBravoFixes() {
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP + 1, FIX_RIDE_HEIGHT.getMeters()));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2 + 1, FIX_RIDE_HEIGHT2.getMeters()));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3 + 1, FIX_RIDE_HEIGHT3.getMeters()));
|
||||
}
|
||||
|
||||
private FixLoaderAndTracker createFixLoaderAndTracker() {
|
||||
|
||||
@@ -507,9 +515,23 @@ public class SensorFixStoreAndLoadTest {
|
||||
}
|
||||
|
||||
protected void testNumberOfRawFixes(Track<?> track, long expected) {
|
||||
track.lockForRead();
|
||||
assertEquals(expected, size(track.getRawFixes()));
|
||||
track.unlockAfterRead();
|
||||
if (expected == 0) {
|
||||
if (track != null) {
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertTrue(size(track.getRawFixes()) == 0);
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertEquals(expected, size(track.getRawFixes()));
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DoubleVectorFix createBravoDoubleVectorFixWithRideHeight(long timestamp, double rideHeight) {
|
||||
@@ -671,4 +693,100 @@ public class SensorFixStoreAndLoadTest {
|
||||
assertNull(trackedRace.getSensorTrack(compNotPartOfRace, BravoFixTrack.TRACK_NAME));
|
||||
fixLoaderAndTracker.stop(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
/** Test for changes of bug 4044 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4044 */
|
||||
public void testThatNoSensorFixesAreLoadedAsLongAsStartOfTrackingIsNull() throws InterruptedException {
|
||||
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
|
||||
|
||||
addBravoFixes();
|
||||
|
||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||
|
||||
// raceLog is intentionally not attached
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
|
||||
// No fixes are loaded because startOfTracking isn't set through the raceLog yet
|
||||
assertNull(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME));
|
||||
|
||||
// Loading of fixes is triggered by setting startOfTracking
|
||||
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(START_OF_TRACKING));
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
|
||||
|
||||
fixLoaderAndTracker.stop(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
/** Test for changes of bug 4044 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4044 */
|
||||
public void testThatNoSensorFixesAreRecordedAsWhenStartOfTrackingIsNull() throws InterruptedException {
|
||||
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
|
||||
|
||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||
|
||||
// raceLog is intentionally not attached
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
|
||||
addBravoFixes();
|
||||
|
||||
assertNull(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME));
|
||||
|
||||
fixLoaderAndTracker.stop(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
/** Test for changes of bug 4044 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4044 */
|
||||
public void testThatNoMoreSensorFixesAreLoadedWhenStartOfTrackingChangesToNull() throws InterruptedException {
|
||||
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
|
||||
|
||||
addBravoFixes();
|
||||
|
||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(START_OF_TRACKING));
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
|
||||
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(null, author, 0));
|
||||
|
||||
assertNull(trackedRace.getStartOfTracking());
|
||||
addMoreBravoFixes();
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
|
||||
// only the initial 3 fixes are available
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
|
||||
|
||||
fixLoaderAndTracker.stop(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
/** Test for changes of bug 4044 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4044 */
|
||||
public void testThatNoMoreSensorFixesAreLoadedWhenStartOfTrackingChangesFromNullBySettingStartTime() throws InterruptedException {
|
||||
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
|
||||
addBravoFixes();
|
||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
|
||||
raceLog.add(new RaceLogStartTimeEventImpl(new MillisecondsTimePoint(START_OF_TRACKING), author, 0,
|
||||
new MillisecondsTimePoint(START_OF_TRACKING)));
|
||||
assertNotNull(trackedRace.getStartOfTracking());
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(null, author, 0));
|
||||
addMoreBravoFixes();
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
// only the initial 3 fixes are available
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
|
||||
fixLoaderAndTracker.stop(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+515
-513
File diff suppressed because it is too large
Load Diff
+264
-223
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMapping
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
@@ -32,6 +33,8 @@ import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackingDataLoader;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
|
||||
@@ -47,19 +50,54 @@ import com.sap.sse.util.ThreadPoolUtil;
|
||||
|
||||
/**
|
||||
* This class listens to RaceLog Events, changes to the race and fix loading events and properly handles mappings and
|
||||
* fix loading.
|
||||
*
|
||||
* fix loading.<br>
|
||||
* The two main responsibility are to 1. load fixes already available in the DB when a race is tracked and 2. add fixes
|
||||
* newly send by trackers to {@link Track}s of a {@link TrackedRace} if the fix is relevant for the race. In order to
|
||||
* save memory we try to keep the set of loaded fixes as constrained as possible. In general the following rules apply:
|
||||
* <ul>
|
||||
* <li>{@link RaceChangeListener},</li>
|
||||
* <li>The fixes loaded to a track for a specific item (Mark, Competitor) are associated to a device that needs to be
|
||||
* mapped to the specific item using a {@link RegattaLogDeviceMappingEvent} in the {@link RegattaLog}. Only fixes that
|
||||
* are associated to the mapped device in the mapped {@link TimeRange} are allowed to be loaded into an item's
|
||||
* {@link Track}</li>
|
||||
* <li>If startOfTracking isn't available for the underlying {@link TrackedRace}, no fixes are loaded at all. This
|
||||
* prevents everything to be loaded in cases where a device is mapped for a whole {@link Regatta}'s time range and a
|
||||
* late race is tracked without startOfTracking being initially available.</li>
|
||||
* <li>If endOfTracking isn't available yet for a race, all fixes after startOfTracking are loaded into the respective
|
||||
* {@link Track}. This case is assumed to only occur in live scenarios where the endOfTracking is defined late while
|
||||
* boats cross the finishing line. This isn't much of a problem because no fixes are available for the future.</li>
|
||||
* <li>No loaded fixes are ever removed from {@link Track}s. Even if the tracking times change to be more restrictive,
|
||||
* the fixes in the time range not covered anymore are held in the {@link Track}.</li>
|
||||
* </ul>
|
||||
* Marks can either be tracked or just pinged using the respective app. If a Mark is pinged early in the morning before
|
||||
* the start of a race, no fix is available for this Mark in the tracked {@link TimeRange}. The semantic of pinging a
|
||||
* Mark is that the position is assumed to be correct until it is pinged again. So in extreme cases this single fix
|
||||
* needs to be used for all races of a day. To ensure this semantic there are additional special rules for
|
||||
* {@link Mark}s:
|
||||
* <ul>
|
||||
* <li>Fixes in the tracking time range are loaded/tracked using the above mentioned rules.</li>
|
||||
* <li>If at least one fix is available, no additional fix is loaded at all. If no fix is available for a {@link Mark}
|
||||
* in the tracking time range, the following rules apply:
|
||||
* <ul>
|
||||
* <li>If no fix isn't available yet before/after the tracking time range, the single fix is loaded that is the best
|
||||
* (nearest) before startOfTracking/after endOfTracking.</li>
|
||||
* <li>If additional fixes get available (either by tracking or by a new device mapping), only the best single fixes are
|
||||
* taken using the rule above. If there is already a best fix before startOfTracking/after endOfTracking, the new best
|
||||
* fix is only used if it is better (nearer) as the existing one.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
* There is a corner cases that is assumed to be acceptable for the specific semantic of a {@link Mark}'s fixes:<br>
|
||||
* If a Marks is tracked and not pinged, any new fix transferred from the tracking device is treated as being better
|
||||
* than the one before. So all fixes are being recorded starting at the time point when the operator starts tracking for
|
||||
* a race and startOfTracking is in the future.<br>
|
||||
* Some related classes:
|
||||
* <ul>
|
||||
* <li>{@link RaceChangeListener}</li>
|
||||
* <li>{@link RegattaLogEventVisitor}</li>
|
||||
* <li>{@link FixReceivedListener}</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @author sschaefe
|
||||
*
|
||||
*/
|
||||
public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
private static final Logger logger = Logger.getLogger(FixLoaderAndTracker.class.getName());
|
||||
protected final DynamicTrackedRace trackedRace;
|
||||
@@ -72,16 +110,24 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
private final AbstractRaceChangeListener raceChangeListener = new AbstractRaceChangeListener() {
|
||||
@Override
|
||||
public void startOfTrackingChanged(TimePoint oldStartOfTracking, TimePoint newStartOfTracking) {
|
||||
if ((newStartOfTracking == null
|
||||
|| (oldStartOfTracking != null && newStartOfTracking.before(oldStartOfTracking)))) {
|
||||
loadFixesForExtendedTimeRange(newStartOfTracking, oldStartOfTracking);
|
||||
if (newStartOfTracking != null) {
|
||||
if (oldStartOfTracking == null) {
|
||||
// Fixes wheren't loaded while startOfTracking was null. So we need to load all fixes in the tracking interval now.
|
||||
loadFixesForExtendedTimeRange(getTrackingTimeRange());
|
||||
} else if (newStartOfTracking.before(oldStartOfTracking)) {
|
||||
loadFixesForExtendedTimeRange(new TimeRangeImpl(newStartOfTracking, oldStartOfTracking));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endOfTrackingChanged(TimePoint oldEndOfTracking, TimePoint newEndOfTracking) {
|
||||
if (newEndOfTracking == null || (oldEndOfTracking != null && newEndOfTracking.after(oldEndOfTracking))) {
|
||||
loadFixesForExtendedTimeRange(oldEndOfTracking, newEndOfTracking);
|
||||
if (trackedRace.getStartOfTracking() != null) {
|
||||
if (newEndOfTracking == null && oldEndOfTracking != null) {
|
||||
loadFixesForExtendedTimeRange(new TimeRangeImpl(oldEndOfTracking, TimePoint.EndOfTime));
|
||||
} else if (newEndOfTracking != null && oldEndOfTracking != null && oldEndOfTracking.before(newEndOfTracking)) {
|
||||
loadFixesForExtendedTimeRange(new TimeRangeImpl(oldEndOfTracking, newEndOfTracking));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,107 +138,106 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
private final FixReceivedListener<Timed> listener = new FixReceivedListener<Timed>() {
|
||||
@Override
|
||||
public void fixReceived(DeviceIdentifier device, Timed fix) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
final TimePoint timePoint = fix.getTimePoint();
|
||||
deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), (mapping) -> {
|
||||
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
if (!preemptiveStopRequested.get() && trackedRace.getStartOfTracking() != null) {
|
||||
final TimePoint timePoint = fix.getTimePoint();
|
||||
deviceMappings.forEachMappingOfDeviceIncludingTimePoint(device, fix.getTimePoint(), (mapping) -> {
|
||||
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
|
||||
.createCompetitorMapper(event.getClass());
|
||||
DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace,
|
||||
event.getMappedTo());
|
||||
if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
|
||||
mapper.addFix(track, (DoubleVectorFix) fix);
|
||||
}
|
||||
}
|
||||
SensorFixMapper<SensorFix, DynamicSensorFixTrack<Competitor, SensorFix>, Competitor> mapper = sensorFixMapperFactory
|
||||
.createCompetitorMapper(event.getClass());
|
||||
DynamicSensorFixTrack<Competitor, SensorFix> track = mapper.getTrack(trackedRace,
|
||||
event.getMappedTo());
|
||||
if (track != null && trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
|
||||
mapper.addFix(track, (DoubleVectorFix) fix);
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
Competitor comp = event.getMappedTo();
|
||||
if (fix instanceof GPSFixMoving) {
|
||||
trackedRace.recordFix(comp, (GPSFixMoving) fix);
|
||||
} else {
|
||||
logger.log(Level.WARNING,
|
||||
String.format(
|
||||
"Could not add fix for competitor (%s) in race (%s), as it"
|
||||
+ " is no GPSFixMoving, meaning it is missing COG/SOG values",
|
||||
comp, trackedRace.getRace().getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
Competitor comp = event.getMappedTo();
|
||||
if (fix instanceof GPSFixMoving) {
|
||||
trackedRace.recordFix(comp, (GPSFixMoving) fix);
|
||||
} else {
|
||||
logger.log(Level.WARNING,
|
||||
String.format(
|
||||
"Could not add fix for competitor (%s) in race (%s), as it"
|
||||
+ " is no GPSFixMoving, meaning it is missing COG/SOG values",
|
||||
comp, trackedRace.getRace().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
Mark mark = event.getMappedTo();
|
||||
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark);
|
||||
final GPSFix firstFixAtOrAfter;
|
||||
final boolean forceFix;
|
||||
if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
|
||||
forceFix = false;
|
||||
} else {
|
||||
markTrack.lockForRead();
|
||||
try {
|
||||
if (Util.isEmpty(markTrack.getRawFixes())
|
||||
|| (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null
|
||||
&& firstFixAtOrAfter.getTimePoint().equals(timePoint)) {
|
||||
// either the first fix or overwriting an existing one
|
||||
forceFix = true;
|
||||
} else {
|
||||
// checking if the given fix is "better" than an existing one
|
||||
TimePoint startOfTracking = trackedRace.getStartOfTracking();
|
||||
TimePoint endOfTracking = trackedRace.getStartOfTracking();
|
||||
if (startOfTracking != null) {
|
||||
GPSFix fixAfterStartOfTracking = markTrack
|
||||
.getFirstFixAtOrAfter(startOfTracking);
|
||||
if (fixAfterStartOfTracking == null
|
||||
|| !trackedRace.isWithinStartAndEndOfTracking(
|
||||
fixAfterStartOfTracking.getTimePoint())) {
|
||||
// There is no fix in the tracking interval, so this fix could be "better"
|
||||
// than ones already available in the track
|
||||
// Better means closer before/after the beginning/end of the tracking
|
||||
// interval
|
||||
if (timePoint.before(startOfTracking)) {
|
||||
// check if it is closer to the beginning of the tracking interval
|
||||
GPSFix fixBeforeStartOfTracking = markTrack
|
||||
.getLastFixAtOrBefore(startOfTracking);
|
||||
forceFix = (fixBeforeStartOfTracking == null
|
||||
|| fixBeforeStartOfTracking.getTimePoint().before(timePoint));
|
||||
} else if (endOfTracking != null && timePoint.after(endOfTracking)) {
|
||||
// check if it is closer to the end of the tracking interval
|
||||
GPSFix fixAfterEndOfTracking = markTrack
|
||||
.getFirstFixAtOrAfter(endOfTracking);
|
||||
forceFix = (fixAfterEndOfTracking == null
|
||||
|| fixAfterEndOfTracking.getTimePoint().after(timePoint));
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
Mark mark = event.getMappedTo();
|
||||
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getOrCreateTrack(mark);
|
||||
final GPSFix firstFixAtOrAfter;
|
||||
final boolean forceFix;
|
||||
if (trackedRace.isWithinStartAndEndOfTracking(fix.getTimePoint())) {
|
||||
forceFix = false;
|
||||
} else {
|
||||
markTrack.lockForRead();
|
||||
try {
|
||||
if (Util.isEmpty(markTrack.getRawFixes())
|
||||
|| (firstFixAtOrAfter = markTrack.getFirstFixAtOrAfter(timePoint)) != null
|
||||
&& firstFixAtOrAfter.getTimePoint().equals(timePoint)) {
|
||||
// either the first fix or overwriting an existing one
|
||||
forceFix = true;
|
||||
} else {
|
||||
// checking if the given fix is "better" than an existing one
|
||||
TimePoint startOfTracking = trackedRace.getStartOfTracking();
|
||||
TimePoint endOfTracking = trackedRace.getStartOfTracking();
|
||||
if (startOfTracking != null) {
|
||||
GPSFix fixAfterStartOfTracking = markTrack
|
||||
.getFirstFixAtOrAfter(startOfTracking);
|
||||
if (fixAfterStartOfTracking == null
|
||||
|| !trackedRace.isWithinStartAndEndOfTracking(
|
||||
fixAfterStartOfTracking.getTimePoint())) {
|
||||
// There is no fix in the tracking interval, so this fix could be "better"
|
||||
// than ones already available in the track
|
||||
// Better means closer before/after the beginning/end of the tracking
|
||||
// interval
|
||||
if (timePoint.before(startOfTracking)) {
|
||||
// check if it is closer to the beginning of the tracking interval
|
||||
GPSFix fixBeforeStartOfTracking = markTrack
|
||||
.getLastFixAtOrBefore(startOfTracking);
|
||||
forceFix = (fixBeforeStartOfTracking == null
|
||||
|| fixBeforeStartOfTracking.getTimePoint().before(timePoint));
|
||||
} else if (endOfTracking != null && timePoint.after(endOfTracking)) {
|
||||
// check if it is closer to the end of the tracking interval
|
||||
GPSFix fixAfterEndOfTracking = markTrack
|
||||
.getFirstFixAtOrAfter(endOfTracking);
|
||||
forceFix = (fixAfterEndOfTracking == null
|
||||
|| fixAfterEndOfTracking.getTimePoint().after(timePoint));
|
||||
} else {
|
||||
forceFix = false;
|
||||
}
|
||||
} else {
|
||||
// there is already a fix in the tracking interval
|
||||
forceFix = false;
|
||||
}
|
||||
} else {
|
||||
// there is already a fix in the tracking interval
|
||||
forceFix = false;
|
||||
}
|
||||
} else {
|
||||
forceFix = false;
|
||||
}
|
||||
} finally {
|
||||
markTrack.unlockAfterRead();
|
||||
}
|
||||
} finally {
|
||||
markTrack.unlockAfterRead();
|
||||
}
|
||||
trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix);
|
||||
}
|
||||
trackedRace.recordFix(mark, (GPSFix) fix, /* only when in tracking interval */ !forceFix);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -215,20 +260,22 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
*/
|
||||
private void loadFixesForNewlyCoveredTimeRanges(WithID item,
|
||||
Map<RegattaLogDeviceMappingEvent<WithID>, MultiTimeRange> newlyCoveredTimeRanges) {
|
||||
TimeRange trackingTimeRange = getTrackingTimeRange();
|
||||
loadFixesInTrackingTimeRange(newlyCoveredTimeRanges, trackingTimeRange);
|
||||
if (item instanceof Mark) {
|
||||
Mark mark = (Mark) item;
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
||||
// load all mapped fixes if there was no fix in the tracking TimeRange
|
||||
GPSFix firstFixAfterStartOfTracking = track.getFirstFixAfter(trackingTimeRange.from());
|
||||
if (firstFixAfterStartOfTracking == null
|
||||
|| firstFixAfterStartOfTracking.getTimePoint().after(trackingTimeRange.to())) {
|
||||
// There is no fix in the tracking interval -> looking for better fixes before start of tracking and
|
||||
// after end of tracking
|
||||
newlyCoveredTimeRanges.forEach((event, timeRange) -> {
|
||||
loadBetterFixesIfAvailable(trackingTimeRange, timeRange, event);
|
||||
});
|
||||
if (trackedRace.getStartOfTracking() != null) {
|
||||
TimeRange trackingTimeRange = getTrackingTimeRange();
|
||||
loadFixesInTrackingTimeRange(newlyCoveredTimeRanges, trackingTimeRange);
|
||||
if (item instanceof Mark) {
|
||||
Mark mark = (Mark) item;
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
||||
// load all mapped fixes if there was no fix in the tracking TimeRange
|
||||
GPSFix firstFixAfterStartOfTracking = track.getFirstFixAfter(trackingTimeRange.from());
|
||||
if (firstFixAfterStartOfTracking == null
|
||||
|| firstFixAfterStartOfTracking.getTimePoint().after(trackingTimeRange.to())) {
|
||||
// There is no fix in the tracking interval -> looking for better fixes before start of tracking and
|
||||
// after end of tracking
|
||||
newlyCoveredTimeRanges.forEach((event, timeRange) -> {
|
||||
loadBetterFixesIfAvailable(trackingTimeRange, timeRange, event);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,7 +309,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
*/
|
||||
public boolean containsMappingThatIntersectsTimeRange(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, TimeRange timeRange) {
|
||||
for (DeviceMappingWithRegattaLogEvent<WithID> mapping : mappings) {
|
||||
if(timeRange.intersects(mapping.getTimeRange())) {
|
||||
if (timeRange.intersects(mapping.getTimeRange())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -273,60 +320,56 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
* Loads the fixes in the specified {@link TimeRange} using a visitor of the given mapping event.
|
||||
*/
|
||||
private void loadFixes(TimeRange timeRangeToLoad, RegattaLogDeviceMappingEvent<? extends WithID> mappingEvent) {
|
||||
if (timeRangeToLoad == null) {
|
||||
return;
|
||||
}
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
mappingEvent.accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
SensorFixMapper<Timed, DynamicTrack<Timed>, Competitor> mapper = sensorFixMapperFactory
|
||||
.createCompetitorMapper(event.getClass());
|
||||
DynamicTrack<Timed> track = mapper.getTrack(trackedRace, event.getMappedTo());
|
||||
if (track != null) {
|
||||
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
try {
|
||||
sensorFixStore.<DoubleVectorFix> loadFixes(fix -> mapper.addFix(track, fix), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (NoCorrespondingServiceRegisteredException | TransformationException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for competitor: " + event.getMappedTo()
|
||||
+ "; device: " + event.getDevice());
|
||||
if (timeRangeToLoad != null && !preemptiveStopRequested.get()) {
|
||||
mappingEvent.accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
SensorFixMapper<Timed, DynamicTrack<Timed>, Competitor> mapper = sensorFixMapperFactory
|
||||
.createCompetitorMapper(event.getClass());
|
||||
DynamicTrack<Timed> track = mapper.getTrack(trackedRace, event.getMappedTo());
|
||||
if (track != null) {
|
||||
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
try {
|
||||
sensorFixStore.<DoubleVectorFix> loadFixes(fix -> mapper.addFix(track, fix), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (NoCorrespondingServiceRegisteredException | TransformationException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for competitor: " + event.getMappedTo()
|
||||
+ "; device: " + event.getDevice());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(event.getMappedTo());
|
||||
if (track != null) {
|
||||
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(event.getMappedTo());
|
||||
if (track != null) {
|
||||
// for split-fleet racing, device mappings coming from the regatta log may not be relevant
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
try {
|
||||
sensorFixStore.<GPSFixMoving> loadFixes(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load competitor track " + event.getMappedTo() + "; device "
|
||||
+ event.getDevice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
try {
|
||||
sensorFixStore.<GPSFixMoving> loadFixes(fix -> track.add(fix, true), event.getDevice(),
|
||||
sensorFixStore.<GPSFix> loadFixes(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load competitor track " + event.getMappedTo() + "; device "
|
||||
+ event.getDevice());
|
||||
logger.log(Level.WARNING, "Could not load mark track " + event.getMappedTo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
try {
|
||||
sensorFixStore.<GPSFix> loadFixes(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load mark track " + event.getMappedTo());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,66 +377,65 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
*/
|
||||
private void loadBetterFixesIfAvailable(TimeRange trackingTimeRange, MultiTimeRange coveredTimeRanges,
|
||||
RegattaLogDeviceMappingEvent<? extends WithID> mappingEvent) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
if (!preemptiveStopRequested.get()) {
|
||||
mappingEvent.accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
|
||||
final GPSFix lastFixAtOrBeforeStartOfTracking = track.getLastFixAtOrBefore(trackingTimeRange.from());
|
||||
// A better fix before start of tracking must be after the current best fix
|
||||
final MultiTimeRange beforeRange = coveredTimeRanges
|
||||
.intersection(new TimeRangeImpl(
|
||||
lastFixAtOrBeforeStartOfTracking != null
|
||||
? lastFixAtOrBeforeStartOfTracking.getTimePoint() : TimePoint.BeginningOfTime,
|
||||
trackingTimeRange.from()));
|
||||
// starting to load newer ranges to make the first found fix the best available fix
|
||||
Collection<TimeRange> inverseTimeRanges = Util.addAll(beforeRange,
|
||||
new TreeSet<>((timeRange1, timeRange2) -> -timeRange1.from().compareTo(timeRange2.from())));
|
||||
for (TimeRange timeRange : inverseTimeRanges) {
|
||||
try {
|
||||
if (sensorFixStore.<GPSFix> loadYoungestFix(fix -> track.add(fix, true),
|
||||
event.getDevice(), timeRange)) {
|
||||
// new best fix before start of tracking found
|
||||
break;
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load better fix for mark track " + event.getMappedTo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final GPSFix firstFixAtOrAfterEndOfTracking = track.getFirstFixAtOrAfter(trackingTimeRange.to());
|
||||
// A better fix after end of tracking must be before the current best fix
|
||||
MultiTimeRange afterRange = coveredTimeRanges.intersection(new TimeRangeImpl(trackingTimeRange.to(),
|
||||
firstFixAtOrAfterEndOfTracking != null ? firstFixAtOrAfterEndOfTracking.getTimePoint()
|
||||
: TimePoint.EndOfTime));
|
||||
for (TimeRange timeRange : afterRange) {
|
||||
try {
|
||||
if (sensorFixStore.<GPSFix> loadOldestFix(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRange)) {
|
||||
// new best fix after end of tracking found
|
||||
break;
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load better fix for mark track " + event.getMappedTo());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
mappingEvent.accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
|
||||
final GPSFix lastFixAtOrBeforeStartOfTracking = track.getLastFixAtOrBefore(trackingTimeRange.from());
|
||||
// A better fix before start of tracking must be after the current best fix
|
||||
final MultiTimeRange beforeRange = coveredTimeRanges
|
||||
.intersection(new TimeRangeImpl(
|
||||
lastFixAtOrBeforeStartOfTracking != null
|
||||
? lastFixAtOrBeforeStartOfTracking.getTimePoint() : TimePoint.BeginningOfTime,
|
||||
trackingTimeRange.from()));
|
||||
// starting to load newer ranges to make the first found fix the best available fix
|
||||
Collection<TimeRange> inverseTimeRanges = Util.addAll(beforeRange,
|
||||
new TreeSet<>((timeRange1, timeRange2) -> -timeRange1.from().compareTo(timeRange2.from())));
|
||||
for (TimeRange timeRange : inverseTimeRanges) {
|
||||
try {
|
||||
if (sensorFixStore.<GPSFix> loadYoungestFix(fix -> track.add(fix, true),
|
||||
event.getDevice(), timeRange)) {
|
||||
// new best fix before start of tracking found
|
||||
break;
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load better fix for mark track " + event.getMappedTo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final GPSFix firstFixAtOrAfterEndOfTracking = track.getFirstFixAtOrAfter(trackingTimeRange.to());
|
||||
// A better fix after end of tracking must be before the current best fix
|
||||
MultiTimeRange afterRange = coveredTimeRanges.intersection(new TimeRangeImpl(trackingTimeRange.to(),
|
||||
firstFixAtOrAfterEndOfTracking != null ? firstFixAtOrAfterEndOfTracking.getTimePoint()
|
||||
: TimePoint.EndOfTime));
|
||||
for (TimeRange timeRange : afterRange) {
|
||||
try {
|
||||
if (sensorFixStore.<GPSFix> loadOldestFix(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRange)) {
|
||||
// new best fix after end of tracking found
|
||||
break;
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load better fix for mark track " + event.getMappedTo());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TimeRange getTrackingTimeRange() {
|
||||
@@ -435,8 +477,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFixesForExtendedTimeRange(TimePoint loadFixesFrom, TimePoint loadFixesTo) {
|
||||
final TimeRangeImpl extendedTimeRange = new TimeRangeImpl(loadFixesFrom, loadFixesTo);
|
||||
private void loadFixesForExtendedTimeRange(final TimeRange extendedTimeRange) {
|
||||
deviceMappings.forEachItemAndCoveredTimeRanges((item, mappingsAndCoveredTimeRanges) -> loadFixesInTrackingTimeRange(mappingsAndCoveredTimeRanges, extendedTimeRange));
|
||||
}
|
||||
|
||||
|
||||
+170
-170
@@ -1,170 +1,170 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.TabPanel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
|
||||
|
||||
public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAndFleetsDialog {
|
||||
protected static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
|
||||
protected static class RegattaParameterValidator implements Validator<RegattaDTO> {
|
||||
private StringMessages stringMessages;
|
||||
private ArrayList<RegattaDTO> existingRegattas;
|
||||
|
||||
public RegattaParameterValidator(StringMessages stringMessages, Collection<RegattaDTO> existingRegattas) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.existingRegattas = new ArrayList<RegattaDTO>(existingRegattas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(RegattaDTO regattaToValidate) {
|
||||
String errorMessage = null;
|
||||
boolean nameNotEmpty = regattaToValidate.getName() != null && regattaToValidate.getName().length() > 0;
|
||||
boolean boatClassNotEmpty = regattaToValidate.boatClass != null
|
||||
&& regattaToValidate.boatClass.getName().length() > 0;
|
||||
boolean unique = true;
|
||||
for (RegattaDTO regatta : existingRegattas) {
|
||||
if (regatta.getName().equals(regattaToValidate.getName())) {
|
||||
unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Date startDate = regattaToValidate.startDate;
|
||||
Date endDate = regattaToValidate.endDate;
|
||||
String datesErrorMessage = null;
|
||||
// remark: startDate == null and endDate == null is valid
|
||||
if (startDate != null && endDate != null) {
|
||||
if (startDate.after(endDate)) {
|
||||
datesErrorMessage = stringMessages.pleaseEnterStartAndEndDate();
|
||||
}
|
||||
} else if ((startDate != null && endDate == null) || (startDate == null && endDate != null)) {
|
||||
datesErrorMessage = stringMessages.pleaseEnterStartAndEndDate();
|
||||
}
|
||||
|
||||
if (datesErrorMessage != null) {
|
||||
errorMessage = datesErrorMessage;
|
||||
} else if (!nameNotEmpty) {
|
||||
errorMessage = stringMessages.pleaseEnterAName();
|
||||
} else if (regattaToValidate.getName().contains("/")) {
|
||||
errorMessage = stringMessages.regattaNameMustNotContainSlashes();
|
||||
} else if (!boatClassNotEmpty) {
|
||||
errorMessage = stringMessages.pleaseEnterABoatClass();
|
||||
} else if (!unique) {
|
||||
errorMessage = stringMessages.regattaWithThisNameAlreadyExists();
|
||||
}
|
||||
|
||||
if (regattaToValidate.buoyZoneRadiusInHullLengths == null) {
|
||||
errorMessage = stringMessages.incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
|
||||
}
|
||||
|
||||
if (errorMessage == null) {
|
||||
List<SeriesDTO> seriesToValidate = regattaToValidate.series;
|
||||
int index = 0;
|
||||
boolean seriesNameNotEmpty = true;
|
||||
for (SeriesDTO series : seriesToValidate) {
|
||||
seriesNameNotEmpty = series.getName() != null && series.getName().length() > 0;
|
||||
if (!seriesNameNotEmpty) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
int index2 = 0;
|
||||
boolean seriesUnique = true;
|
||||
HashSet<String> setToFindDuplicates = new HashSet<String>();
|
||||
for (SeriesDTO series : seriesToValidate) {
|
||||
if (!setToFindDuplicates.add(series.getName())) {
|
||||
seriesUnique = false;
|
||||
break;
|
||||
}
|
||||
index2++;
|
||||
}
|
||||
if (!seriesNameNotEmpty) {
|
||||
errorMessage = stringMessages.series() + " " + (index + 1) + ": "
|
||||
+ stringMessages.pleaseEnterAName();
|
||||
} else if (!seriesUnique) {
|
||||
errorMessage = stringMessages.series() + " " + (index2 + 1) + ": "
|
||||
+ stringMessages.seriesWithThisNameAlreadyExists();
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public RegattaWithSeriesAndFleetsCreateDialog(Collection<RegattaDTO> existingRegattas,
|
||||
List<EventDTO> existingEvents, EventDTO correspondingEvent, StringMessages stringMessages, DialogCallback<RegattaDTO> callback) {
|
||||
super(new RegattaDTO(), Collections.<SeriesDTO>emptySet(), existingEvents, correspondingEvent, stringMessages.addRegatta(), stringMessages.ok(),
|
||||
stringMessages, new RegattaParameterValidator(stringMessages, existingRegattas), callback);
|
||||
buoyZoneRadiusInHullLengthsDoubleBox.setValue(Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS);
|
||||
SeriesDTO series = new SeriesDTO();
|
||||
series.setName(Series.DEFAULT_NAME);
|
||||
series.setMedal(false);
|
||||
series.setStartsWithZeroScore(false);
|
||||
series.setSplitFleetContiguousScoring(false);
|
||||
series.setFirstColumnIsNonDiscardableCarryForward(false);
|
||||
series.setFleets(Collections.singletonList(new FleetDTO(LeaderboardNameConstants.DEFAULT_FLEET_NAME, 0, null)));
|
||||
seriesEditor.setValue(Collections.singleton(series));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnableFleetRemoval() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupAdditionalWidgetsOnPanel(final VerticalPanel panel, Grid formGrid) {
|
||||
super.setupAdditionalWidgetsOnPanel(panel, formGrid);
|
||||
insertRankingMetricTabPanel(formGrid);
|
||||
final TabPanel tabPanel = new TabPanel();
|
||||
tabPanel.setWidth("100%");
|
||||
tabPanel.add(getSeriesEditor(), stringMessages.series());
|
||||
tabPanel.selectTab(0);
|
||||
panel.add(tabPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FocusWidget getInitialFocusWidget() {
|
||||
return nameEntryField;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegattaDTO getResult() {
|
||||
RegattaDTO dto = super.getResult();
|
||||
List<SeriesDTO> seriesList = getSeriesEditor().getValue();
|
||||
for (SeriesDTO series : seriesList) {
|
||||
// generate 3 Default Races if default series is still present
|
||||
if (series.getName().equals(Series.DEFAULT_NAME)) {
|
||||
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
RaceColumnDTO raceColumnDTO = new RaceColumnInSeriesDTO(series.getName(), dto.getName());
|
||||
raceColumnDTO.setName("R"+i);
|
||||
races.add(raceColumnDTO);
|
||||
}
|
||||
series.setRaceColumns(races);
|
||||
}
|
||||
}
|
||||
dto.series = seriesList;
|
||||
setRankingMetrics(dto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
}
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.TabPanel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
|
||||
|
||||
public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAndFleetsDialog {
|
||||
protected static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
|
||||
protected static class RegattaParameterValidator implements Validator<RegattaDTO> {
|
||||
private StringMessages stringMessages;
|
||||
private ArrayList<RegattaDTO> existingRegattas;
|
||||
|
||||
public RegattaParameterValidator(StringMessages stringMessages, Collection<RegattaDTO> existingRegattas) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.existingRegattas = new ArrayList<RegattaDTO>(existingRegattas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(RegattaDTO regattaToValidate) {
|
||||
String errorMessage = null;
|
||||
boolean nameNotEmpty = regattaToValidate.getName() != null && regattaToValidate.getName().length() > 0;
|
||||
boolean boatClassNotEmpty = regattaToValidate.boatClass != null
|
||||
&& regattaToValidate.boatClass.getName().length() > 0;
|
||||
boolean unique = true;
|
||||
for (RegattaDTO regatta : existingRegattas) {
|
||||
if (regatta.getName().equals(regattaToValidate.getName())) {
|
||||
unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Date startDate = regattaToValidate.startDate;
|
||||
Date endDate = regattaToValidate.endDate;
|
||||
String datesErrorMessage = null;
|
||||
// remark: startDate == null and endDate == null is valid
|
||||
if (startDate != null && endDate != null) {
|
||||
if (startDate.after(endDate)) {
|
||||
datesErrorMessage = stringMessages.pleaseEnterStartAndEndDate();
|
||||
}
|
||||
} else if ((startDate != null && endDate == null) || (startDate == null && endDate != null)) {
|
||||
datesErrorMessage = stringMessages.pleaseEnterStartAndEndDate();
|
||||
}
|
||||
|
||||
if (datesErrorMessage != null) {
|
||||
errorMessage = datesErrorMessage;
|
||||
} else if (!nameNotEmpty) {
|
||||
errorMessage = stringMessages.pleaseEnterAName();
|
||||
} else if (regattaToValidate.getName().contains("/")) {
|
||||
errorMessage = stringMessages.regattaNameMustNotContainSlashes();
|
||||
} else if (!boatClassNotEmpty) {
|
||||
errorMessage = stringMessages.pleaseEnterABoatClass();
|
||||
} else if (!unique) {
|
||||
errorMessage = stringMessages.regattaWithThisNameAlreadyExists();
|
||||
}
|
||||
|
||||
if (regattaToValidate.buoyZoneRadiusInHullLengths == null) {
|
||||
errorMessage = stringMessages.incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
|
||||
}
|
||||
|
||||
if (errorMessage == null) {
|
||||
List<SeriesDTO> seriesToValidate = regattaToValidate.series;
|
||||
int index = 0;
|
||||
boolean seriesNameNotEmpty = true;
|
||||
for (SeriesDTO series : seriesToValidate) {
|
||||
seriesNameNotEmpty = series.getName() != null && series.getName().length() > 0;
|
||||
if (!seriesNameNotEmpty) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
int index2 = 0;
|
||||
boolean seriesUnique = true;
|
||||
HashSet<String> setToFindDuplicates = new HashSet<String>();
|
||||
for (SeriesDTO series : seriesToValidate) {
|
||||
if (!setToFindDuplicates.add(series.getName())) {
|
||||
seriesUnique = false;
|
||||
break;
|
||||
}
|
||||
index2++;
|
||||
}
|
||||
if (!seriesNameNotEmpty) {
|
||||
errorMessage = stringMessages.series() + " " + (index + 1) + ": "
|
||||
+ stringMessages.pleaseEnterAName();
|
||||
} else if (!seriesUnique) {
|
||||
errorMessage = stringMessages.series() + " " + (index2 + 1) + ": "
|
||||
+ stringMessages.seriesWithThisNameAlreadyExists();
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public RegattaWithSeriesAndFleetsCreateDialog(Collection<RegattaDTO> existingRegattas,
|
||||
List<EventDTO> existingEvents, EventDTO correspondingEvent, StringMessages stringMessages, DialogCallback<RegattaDTO> callback) {
|
||||
super(new RegattaDTO(), Collections.<SeriesDTO>emptySet(), existingEvents, correspondingEvent, stringMessages.addRegatta(), stringMessages.ok(),
|
||||
stringMessages, new RegattaParameterValidator(stringMessages, existingRegattas), callback);
|
||||
buoyZoneRadiusInHullLengthsDoubleBox.setValue(Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS);
|
||||
SeriesDTO series = new SeriesDTO();
|
||||
series.setName(Series.DEFAULT_NAME);
|
||||
series.setMedal(false);
|
||||
series.setStartsWithZeroScore(false);
|
||||
series.setSplitFleetContiguousScoring(false);
|
||||
series.setFirstColumnIsNonDiscardableCarryForward(false);
|
||||
series.setFleets(Collections.singletonList(new FleetDTO(LeaderboardNameConstants.DEFAULT_FLEET_NAME, 0, null)));
|
||||
seriesEditor.setValue(Collections.singleton(series));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnableFleetRemoval() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupAdditionalWidgetsOnPanel(final VerticalPanel panel, Grid formGrid) {
|
||||
super.setupAdditionalWidgetsOnPanel(panel, formGrid);
|
||||
insertRankingMetricTabPanel(formGrid);
|
||||
final TabPanel tabPanel = new TabPanel();
|
||||
tabPanel.setWidth("100%");
|
||||
tabPanel.add(getSeriesEditor(), stringMessages.series());
|
||||
tabPanel.selectTab(0);
|
||||
panel.add(tabPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FocusWidget getInitialFocusWidget() {
|
||||
return nameEntryField;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegattaDTO getResult() {
|
||||
RegattaDTO dto = super.getResult();
|
||||
List<SeriesDTO> seriesList = getSeriesEditor().getValue();
|
||||
for (SeriesDTO series : seriesList) {
|
||||
// generate 3 Default Races if default series is still present
|
||||
if (series.getName().equals(Series.DEFAULT_NAME)) {
|
||||
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
RaceColumnDTO raceColumnDTO = new RaceColumnInSeriesDTO(series.getName(), dto.getName());
|
||||
raceColumnDTO.setName("R"+i);
|
||||
races.add(raceColumnDTO);
|
||||
}
|
||||
series.setRaceColumns(races);
|
||||
}
|
||||
}
|
||||
dto.series = seriesList;
|
||||
setRankingMetrics(dto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user