mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-18 11:49:09 +00:00
store the GPS fix received last for each device in the metadata collection
This commit is contained in:
+1
-1
@@ -145,7 +145,7 @@ public enum FieldNames {
|
||||
DEVICE_ID, GPSFIX_TYPE, GPSFIX,
|
||||
|
||||
// GPSFixes metadata
|
||||
TIMERANGE, NUM_FIXES,
|
||||
TIMERANGE, NUM_FIXES, LAST_FIX_RECEIVED,
|
||||
|
||||
// Timespan
|
||||
FROM_MILLIS, TO_MILLIS,
|
||||
|
||||
+50
-40
@@ -195,37 +195,28 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
final Object dbDeviceId = storeDeviceId(deviceServiceFinder, device);
|
||||
final int nrOfTotalFixes = Util.size(fixes);
|
||||
final ArrayList<Document> dbFixes = new ArrayList<>(nrOfTotalFixes);
|
||||
|
||||
TimePoint newFrom = null;
|
||||
TimePoint newTo = null;
|
||||
final TimeRange oldTimeRange = getTimeRangeCoveredByFixes(device);
|
||||
TimeRange newTimeRange = oldTimeRange;
|
||||
FixT latestFix = null;
|
||||
for (FixT fix : fixes) {
|
||||
String type = fix.getClass().getName();
|
||||
FixMongoHandler<FixT> mongoHandler = findService(type);
|
||||
Object fixObject = mongoHandler.transformForth(fix);
|
||||
Document entry = new Document().append(FieldNames.DEVICE_ID.name(), dbDeviceId)
|
||||
.append(FieldNames.GPSFIX_TYPE.name(), type).append(FieldNames.GPSFIX.name(), fixObject);
|
||||
if (latestFix == null || latestFix.getTimePoint().before(fix.getTimePoint())) {
|
||||
latestFix = fix;
|
||||
}
|
||||
Document entry = new Document().append(FieldNames.DEVICE_ID.name(), dbDeviceId);
|
||||
storeFixToDocument(entry, fix);
|
||||
mongoOF.storeTimed(fix, entry);
|
||||
dbFixes.add(entry);
|
||||
TimePoint fixTP = fix.getTimePoint();
|
||||
if (newFrom == null || newFrom.after(fixTP)) {
|
||||
newFrom = fixTP;
|
||||
}
|
||||
if (newTo == null || newTo.before(fixTP)) {
|
||||
newTo = fixTP;
|
||||
}
|
||||
final TimeRangeImpl fixTimeRange = new TimeRangeImpl(fixTP, fixTP, /* toIsInclusive */ true);
|
||||
newTimeRange = newTimeRange == null ? fixTimeRange : newTimeRange.extend(fixTimeRange);
|
||||
}
|
||||
fixesCollection.withWriteConcern(WriteConcern.UNACKNOWLEDGED).insertMany(dbFixes);
|
||||
final Document updateOperation = new Document();
|
||||
final Document newMetadata = new Document();
|
||||
newMetadata.put(FieldNames.DEVICE_ID.name(), dbDeviceId);
|
||||
|
||||
TimeRange oldTimeRange = getTimeRangeCoveredByFixes(device);
|
||||
if (oldTimeRange != null) {
|
||||
newFrom = oldTimeRange.from().before(newFrom) ? oldTimeRange.from() : newFrom;
|
||||
newTo = oldTimeRange.to().after(newTo) ? oldTimeRange.to() : newTo;
|
||||
if (latestFix != null) {
|
||||
newMetadata.put(FieldNames.LAST_FIX_RECEIVED.name(), storeFixToDocument(new Document(), latestFix));
|
||||
}
|
||||
final TimeRange newTimeRange = new TimeRangeImpl(newFrom, newTo);
|
||||
|
||||
storeTimeRange(newTimeRange, newMetadata, FieldNames.TIMERANGE);
|
||||
updateOperation.append("$set", newMetadata);
|
||||
updateOperation.append("$inc", new Document(FieldNames.NUM_FIXES.name(), nrOfTotalFixes));
|
||||
@@ -239,6 +230,20 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
return maneuverChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the {@code fix} to the {@code entry} document such that {@link #loadFix(Document)} can re-establish
|
||||
* the fix from that document.
|
||||
*
|
||||
* @return for convenience and call chaining, the updated {@code entry} is returned
|
||||
*/
|
||||
private <FixT extends Timed> Document storeFixToDocument(Document entry, FixT fix) throws TransformationException {
|
||||
String type = fix.getClass().getName();
|
||||
FixMongoHandler<FixT> mongoHandler = findService(type);
|
||||
Object fixObject = mongoHandler.transformForth(fix);
|
||||
entry.append(FieldNames.GPSFIX_TYPE.name(), type).append(FieldNames.GPSFIX.name(), fixObject);
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> void storeFix(DeviceIdentifier device, FixT fix) {
|
||||
storeFixes(device, Collections.singletonList(fix));
|
||||
@@ -294,21 +299,27 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
@Override
|
||||
public TimeRange getTimeRangeCoveredByFixes(DeviceIdentifier device)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
Document result = findMetadataObject(device);
|
||||
if (result == null) {
|
||||
return null;
|
||||
final Document resultDocument = findMetadataObject(device);
|
||||
final TimeRange result;
|
||||
if (resultDocument == null) {
|
||||
result = null;
|
||||
} else {
|
||||
result = DomainObjectFactoryImpl.loadTimeRange(resultDocument, FieldNames.TIMERANGE);
|
||||
}
|
||||
return DomainObjectFactoryImpl.loadTimeRange(result, FieldNames.TIMERANGE);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumberOfFixes(DeviceIdentifier device)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
Document result = findMetadataObject(device);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
final Document resultDocument = findMetadataObject(device);
|
||||
final long result;
|
||||
if (resultDocument == null) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = ((Number) resultDocument.get(FieldNames.NUM_FIXES.name())).longValue();
|
||||
}
|
||||
return ((Number) result.get(FieldNames.NUM_FIXES.name())).longValue();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,20 +342,19 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getLastFix(Iterable<DeviceIdentifier> forDevices)
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getFixLastReceived(Iterable<DeviceIdentifier> forDevices)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
Map<DeviceIdentifier, FixT> result = new HashMap<>();
|
||||
for (final DeviceIdentifier deviceIdentifier : forDevices) {
|
||||
final Bson deviceQuery = getDeviceQuery(deviceIdentifier);
|
||||
final Document orderBy = new Document(
|
||||
FieldNames.GPSFIX.name() + "." + FieldNames.TIME_AS_MILLIS.name(), -1);
|
||||
FindIterable<Document> lastFixForDeviceCursor = fixesCollection.find(deviceQuery).sort(orderBy).limit(1);
|
||||
if (lastFixForDeviceCursor.iterator().hasNext()) {
|
||||
final Document lastFixForDeviceDbObject = lastFixForDeviceCursor.iterator().next();
|
||||
final Timed lastFixForDevice = loadFix(lastFixForDeviceDbObject);
|
||||
@SuppressWarnings("unchecked")
|
||||
final FixT lastFixForDeviceTyped = (FixT) lastFixForDevice;
|
||||
result.put(deviceIdentifier, lastFixForDeviceTyped);
|
||||
final Document metadataDoc = findMetadataObject(deviceIdentifier);
|
||||
if (metadataDoc != null) {
|
||||
final Document lastFixForDeviceDbObject = (Document) metadataDoc.get(FieldNames.LAST_FIX_RECEIVED.name());
|
||||
if (lastFixForDeviceDbObject != null) {
|
||||
final Timed lastFixForDevice = loadFix(lastFixForDeviceDbObject);
|
||||
@SuppressWarnings("unchecked")
|
||||
final FixT lastFixForDeviceTyped = (FixT) lastFixForDevice;
|
||||
result.put(deviceIdentifier, lastFixForDeviceTyped);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
+9
-7
@@ -30,6 +30,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
@@ -543,7 +544,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(200, 10, 20, 30, 40));
|
||||
assertEquals(2, store.getNumberOfFixes(device));
|
||||
assertEquals(TimeRangeImpl.create(100, 200), store.getTimeRangeCoveredByFixes(device));
|
||||
assertEquals(TimeRangeImpl.create(100, 200, /* toIsInclusive */ true), store.getTimeRangeCoveredByFixes(device));
|
||||
assertEquals(new DegreePosition(10, 20), ((GPSFix) store.getFixLastReceived(Collections.singleton(device)).get(device)).getPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -551,28 +553,28 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(1100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(2100, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes = store.getLastFix(Collections.singleton(device));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes = store.getFixLastReceived(Collections.singleton(device));
|
||||
assertEquals(1, lastFixes.size());
|
||||
Timed lastFix = lastFixes.get(device);
|
||||
assertEquals(2100, lastFix.getTimePoint().asMillis());
|
||||
store.storeFix(device, createFix(2000, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes2 = store.getLastFix(Collections.singleton(device));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes2 = store.getFixLastReceived(Collections.singleton(device));
|
||||
assertEquals(1, lastFixes2.size());
|
||||
Timed lastFix2 = lastFixes2.get(device);
|
||||
assertEquals(2100, lastFix2.getTimePoint().asMillis());
|
||||
assertEquals(2000, lastFix2.getTimePoint().asMillis()); // not the fix with the latest time point but the fix that was last received by the store
|
||||
store.storeFix(device, createFix(2200, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes3 = store.getLastFix(Collections.singleton(device));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes3 = store.getFixLastReceived(Collections.singleton(device));
|
||||
assertEquals(1, lastFixes3.size());
|
||||
Timed lastFix3 = lastFixes3.get(device);
|
||||
assertEquals(2200, lastFix3.getTimePoint().asMillis());
|
||||
final DeviceIdentifier device2 = new SmartphoneImeiIdentifier("b");
|
||||
store.storeFix(device2, createFix(1200, 10, 20, 30, 40));
|
||||
store.storeFix(device2, createFix(1100, 10, 20, 30, 40));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes4 = store.getLastFix(Arrays.asList(device, device2));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes4 = store.getFixLastReceived(Arrays.asList(device, device2));
|
||||
assertEquals(2, lastFixes4.size());
|
||||
Timed lastFix4 = lastFixes4.get(device);
|
||||
assertEquals(2200, lastFix4.getTimePoint().asMillis());
|
||||
Timed lastFixDevice2 = lastFixes4.get(device2);
|
||||
assertEquals(1200, lastFixDevice2.getTimePoint().asMillis());
|
||||
assertEquals(1100, lastFixDevice2.getTimePoint().asMillis()); // not the fix with the latest time point but the fix that was last received by the store
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -75,6 +75,32 @@ public class TimeRangeTest {
|
||||
assertFalse(one.startsBefore(three));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extendWithTimePoint() {
|
||||
TimeRange one = create(5, 10);
|
||||
assertFalse(one.includes(create(10))); // end is exclusive
|
||||
TimePoint two = create(20);
|
||||
TimeRange extension = one.extend(two);
|
||||
assertTrue(extension.includes(one));
|
||||
assertTrue(extension.includes(two));
|
||||
assertTrue(extension.includes(create(5)));
|
||||
assertTrue(extension.includes(create(10)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extendWithTimeRange() {
|
||||
TimeRange one = create(5, 10);
|
||||
assertFalse(one.includes(create(10))); // end is exclusive
|
||||
TimeRange two = create(20, 30);
|
||||
assertFalse(two.includes(create(30))); // end is exclusive
|
||||
TimeRange extension = one.extend(two);
|
||||
assertTrue(extension.includes(one));
|
||||
assertTrue(extension.includes(two));
|
||||
assertTrue(extension.includes(create(5)));
|
||||
assertTrue(extension.includes(create(10)));
|
||||
assertFalse(extension.includes(create(30)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void union() {
|
||||
TimeRange one = create(5, 10);
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ public enum EmptySensorFixStore implements SensorFixStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getLastFix(Iterable<DeviceIdentifier> forDevices) {
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getFixLastReceived(Iterable<DeviceIdentifier> forDevices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -101,7 +101,13 @@ public interface SensorFixStore {
|
||||
|
||||
long getNumberOfFixes(DeviceIdentifier device) throws TransformationException, NoCorrespondingServiceRegisteredException;
|
||||
|
||||
<FixT extends Timed> Map<DeviceIdentifier, FixT> getLastFix(Iterable<DeviceIdentifier> forDevices) throws TransformationException, NoCorrespondingServiceRegisteredException;
|
||||
/**
|
||||
* Obtains the fixes that were received last for each of the devices specified. For devices that have not delivered
|
||||
* fixes yet, no mapping is created in the resulting map. Note that due to the possibility of out-of-order delivery
|
||||
* the fixes returned may not be the fixes with the latest time stamp for that device.
|
||||
*/
|
||||
<FixT extends Timed> Map<DeviceIdentifier, FixT> getFixLastReceived(Iterable<DeviceIdentifier> forDevices)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException;
|
||||
|
||||
/**
|
||||
* Loads the oldest fix for the given device in the specified {@link TimeRange}.
|
||||
|
||||
+4
-4
@@ -186,7 +186,7 @@ public class UDPExpeditionReceiverTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getLastFix(Iterable<DeviceIdentifier> forDevices)
|
||||
public <FixT extends Timed> Map<DeviceIdentifier, FixT> getFixLastReceived(Iterable<DeviceIdentifier> forDevices)
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException {
|
||||
final Map<DeviceIdentifier, FixT> result = new HashMap<>();
|
||||
for (final Entry<DeviceIdentifier, List<Timed>> fixes : fixesReceived.entrySet()) {
|
||||
@@ -348,10 +348,10 @@ public class UDPExpeditionReceiverTest {
|
||||
final ExpeditionGpsDeviceIdentifier gpsDevice = deviceRegistry.getGpsDeviceIdentifier(0);
|
||||
final ExpeditionSensorDeviceIdentifier sensorDevice = deviceRegistry.getSensorDeviceIdentifier(0);
|
||||
assertTrue(sensorFixStore.getNumberOfFixes(gpsDevice) > 0);
|
||||
assertEquals(-33.907350, ((GPSFixMoving) sensorFixStore.getLastFix(Collections.singleton(gpsDevice)).get(gpsDevice)).getPosition().getLatDeg(), 0.0001);
|
||||
assertEquals(18.419951, ((GPSFixMoving) sensorFixStore.getLastFix(Collections.singleton(gpsDevice)).get(gpsDevice)).getPosition().getLngDeg(), 0.0001);
|
||||
assertEquals(-33.907350, ((GPSFixMoving) sensorFixStore.getFixLastReceived(Collections.singleton(gpsDevice)).get(gpsDevice)).getPosition().getLatDeg(), 0.0001);
|
||||
assertEquals(18.419951, ((GPSFixMoving) sensorFixStore.getFixLastReceived(Collections.singleton(gpsDevice)).get(gpsDevice)).getPosition().getLngDeg(), 0.0001);
|
||||
assertTrue(sensorFixStore.getNumberOfFixes(sensorDevice) > 0);
|
||||
final BravoExtendedFix sensorFix = new BravoExtendedFixImpl((DoubleVectorFix) sensorFixStore.getLastFix(Collections.singleton(sensorDevice)).get(sensorDevice));
|
||||
final BravoExtendedFix sensorFix = new BravoExtendedFixImpl((DoubleVectorFix) sensorFixStore.getFixLastReceived(Collections.singleton(sensorDevice)).get(sensorDevice));
|
||||
assertEquals(1.872, sensorFix.getRake().getDegrees(), 0.000001);
|
||||
assertEquals(18.4, sensorFix.getRudder().getDegrees(), 0.05);
|
||||
}
|
||||
|
||||
+1
-1
@@ -7111,7 +7111,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
}
|
||||
|
||||
private DeviceMappingDTO convertToDeviceMappingDTO(DeviceMapping<?> mapping) throws TransformationException {
|
||||
final Map<DeviceIdentifier, Timed> lastFixes = getService().getSensorFixStore().getLastFix(Collections.singleton(mapping.getDevice()));
|
||||
final Map<DeviceIdentifier, Timed> lastFixes = getService().getSensorFixStore().getFixLastReceived(Collections.singleton(mapping.getDevice()));
|
||||
final Timed lastFix;
|
||||
if (lastFixes != null && lastFixes.containsKey(mapping.getDevice())) {
|
||||
lastFix = lastFixes.get(mapping.getDevice());
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ public class CourseConfigurationResource extends AbstractSailingServerResource {
|
||||
Position lastPosition = null;
|
||||
try {
|
||||
// FIXME terribly slow! Furthermore, looking up by deviceIdentifier only misses any other position sources; use regatta?
|
||||
final Map<DeviceIdentifier, Timed> lastFix = getService().getSensorFixStore().getLastFix(Collections.singleton(deviceIdentifier));
|
||||
final Map<DeviceIdentifier, Timed> lastFix = getService().getSensorFixStore().getFixLastReceived(Collections.singleton(deviceIdentifier));
|
||||
final Timed t = lastFix.get(deviceIdentifier);
|
||||
if (t instanceof GPSFix) {
|
||||
lastPosition = ((GPSFix) t).getPosition();
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ public final class TrackingDeviceStatus {
|
||||
|
||||
public static TrackingDeviceStatus calculateDeviceStatus(DeviceIdentifier deviceIdentifier, RacingEventService service) {
|
||||
try {
|
||||
Map<DeviceIdentifier, Timed> lastFixMap = service.getSensorFixStore().getLastFix(Collections.singletonList(deviceIdentifier));
|
||||
Map<DeviceIdentifier, Timed> lastFixMap = service.getSensorFixStore().getFixLastReceived(Collections.singletonList(deviceIdentifier));
|
||||
Timed lastFix = lastFixMap.get(deviceIdentifier);
|
||||
GPSFix lastGPSFix = lastFix instanceof GPSFix ? (GPSFix) lastFix : null;
|
||||
return new TrackingDeviceStatus(deviceIdentifier, lastGPSFix);
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfi
|
||||
// TODO could as well return the GPSFix object which conveniently would combine the position with the time point which may also be interesting to clients...
|
||||
Position lastPosition = null;
|
||||
try {
|
||||
final Map<DeviceIdentifier, Timed> lastFix = sensorFixStore.getLastFix(Collections.singleton(identifier));
|
||||
final Map<DeviceIdentifier, Timed> lastFix = sensorFixStore.getFixLastReceived(Collections.singleton(identifier));
|
||||
final Timed t = lastFix.get(identifier);
|
||||
if (t instanceof GPSFix) {
|
||||
lastPosition = ((GPSFix) t).getPosition();
|
||||
|
||||
+22
-21
@@ -364,39 +364,40 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
// load passing instruction
|
||||
final PassingInstruction passingInstruction = PassingInstruction
|
||||
.valueOf(bdo.get(FieldNames.WAYPOINT_TEMPLATE_PASSINGINSTRUCTION.name()).toString());
|
||||
|
||||
// load master data
|
||||
final String name = bdo.getString(FieldNames.WAYPOINT_TEMPLATE_CONTROL_POINT_NAME.name());
|
||||
final String shortName = bdo.getString(FieldNames.WAYPOINT_TEMPLATE_CONTROL_POINT_SHORT_NAME.name());
|
||||
|
||||
// load mark templates for control point
|
||||
final ArrayList<?> markTemplateUUIDsDbList = bdo.get(FieldNames.WAYPOINT_TEMPLATE_MARK_TEMPLATES.name(),
|
||||
ArrayList.class);
|
||||
boolean hasParsingError = false;
|
||||
|
||||
final List<MarkTemplate> markTemplates = new ArrayList<>();
|
||||
for (Object obj : markTemplateUUIDsDbList) {
|
||||
final MarkTemplate markTemplate = markTemplateResolver.apply(UUID.fromString(obj.toString()));
|
||||
if (markTemplate == null) {
|
||||
logger.warning(String.format("Could not resolve MarkTemplate with id %s for WaypointTemplate.",
|
||||
obj.toString()));
|
||||
hasParsingError = true;
|
||||
break;
|
||||
} else {
|
||||
markTemplates.add(markTemplate);
|
||||
if (markTemplateUUIDsDbList != null) {
|
||||
for (Object obj : markTemplateUUIDsDbList) {
|
||||
final MarkTemplate markTemplate = markTemplateResolver.apply(UUID.fromString(obj.toString()));
|
||||
if (markTemplate == null) {
|
||||
logger.warning(String.format("Could not resolve MarkTemplate with id %s for WaypointTemplate.",
|
||||
obj.toString()));
|
||||
hasParsingError = true;
|
||||
break;
|
||||
} else {
|
||||
markTemplates.add(markTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
final WaypointTemplate result;
|
||||
if (hasParsingError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// create MarkTemplate or MarkTemplatePairImpl
|
||||
final ControlPointTemplate controlPointTemplate;
|
||||
if (markTemplates.size() == 2) {
|
||||
controlPointTemplate = markPairResolver.apply(new Pair<>(name, shortName), markTemplates);
|
||||
result = null;
|
||||
} else {
|
||||
controlPointTemplate = markTemplates.get(0);
|
||||
// create MarkTemplate or MarkTemplatePairImpl
|
||||
final ControlPointTemplate controlPointTemplate;
|
||||
if (markTemplates.size() == 2) {
|
||||
controlPointTemplate = markPairResolver.apply(new Pair<>(name, shortName), markTemplates);
|
||||
} else {
|
||||
controlPointTemplate = markTemplates.get(0);
|
||||
}
|
||||
result = new WaypointTemplateImpl(controlPointTemplate, passingInstruction);
|
||||
}
|
||||
return new WaypointTemplateImpl(controlPointTemplate, passingInstruction);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,13 @@ public interface TimeRange extends Comparable<TimeRange>, Serializable {
|
||||
|
||||
boolean startsBefore(TimeRange other);
|
||||
|
||||
boolean startsBefore(TimePoint other);
|
||||
|
||||
boolean startsAtOrAfter(TimePoint timePoint);
|
||||
|
||||
boolean startsAfter(TimeRange other);
|
||||
|
||||
boolean startsAfter(TimePoint timePoint);
|
||||
|
||||
boolean endsAfter(TimeRange other);
|
||||
|
||||
@@ -125,4 +129,19 @@ public interface TimeRange extends Comparable<TimeRange>, Serializable {
|
||||
* Short for {@link #from()}.{@link TimePoint#until(TimePoint) until}({@link #to()}).
|
||||
*/
|
||||
Duration getDuration();
|
||||
|
||||
/**
|
||||
* Produces a {@link TimeRange} that {@link TimeRange#includes(TimeRange) includes} {@code this} and
|
||||
* {@link TimeRange#includes(TimePoint) includes} {@code timePoint}. If {@code this} time range already
|
||||
* {@link #includes(TimePoint)} {@code timePoint}, {@code this} is returned.
|
||||
*/
|
||||
TimeRange extend(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Produces a {@link TimeRange} that {@link TimeRange#includes(TimeRange) includes} both, {@code this} and
|
||||
* {@code other}. Other than {@link #union(TimeRange)}, this will also work in case {@code other} does not
|
||||
* {@link #touches(TimeRange) touch} {@code this} time range. If {@code this} already {@link #includes(TimeRange)}
|
||||
* {@code other}, {@code this} time range is returned.
|
||||
*/
|
||||
TimeRange extend(TimeRange other);
|
||||
}
|
||||
@@ -22,6 +22,10 @@ public class TimeRangeImpl extends Util.Pair<TimePoint, TimePoint> implements Ti
|
||||
return new TimeRangeImpl(new MillisecondsTimePoint(fromMillis), new MillisecondsTimePoint(toMillisExclusive));
|
||||
}
|
||||
|
||||
public static TimeRange create(long fromMillis, long toMillis, boolean toIsInclusive) {
|
||||
return new TimeRangeImpl(new MillisecondsTimePoint(fromMillis), new MillisecondsTimePoint(toMillis), toIsInclusive);
|
||||
}
|
||||
|
||||
public TimeRangeImpl(TimePoint from, TimePoint to, boolean toIsInclusive) {
|
||||
this(from, computeInclusiveOrExclusiveTo(to, toIsInclusive));
|
||||
}
|
||||
@@ -121,11 +125,21 @@ public class TimeRangeImpl extends Util.Pair<TimePoint, TimePoint> implements Ti
|
||||
return from().before(other.from());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startsBefore(TimePoint other) {
|
||||
return from().before(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startsAtOrAfter(TimePoint timePoint) {
|
||||
return !from().before(timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startsAfter(TimePoint timePoint) {
|
||||
return from().after(timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startsAfter(TimeRange other) {
|
||||
return startsAtOrAfter(other.to());
|
||||
@@ -225,6 +239,34 @@ public class TimeRangeImpl extends Util.Pair<TimePoint, TimePoint> implements Ti
|
||||
return new MultiTimeRangeImpl(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeRange extend(TimePoint timePoint) {
|
||||
final TimeRange result;
|
||||
if (this.includes(timePoint)) {
|
||||
result = this;
|
||||
} else {
|
||||
if (this.startsAfter(timePoint)) {
|
||||
result = new TimeRangeImpl(timePoint, to());
|
||||
} else {
|
||||
assert this.endsBefore(timePoint);
|
||||
result = new TimeRangeImpl(from(), computeInclusiveOrExclusiveTo(timePoint, /* isInclusive */ true));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeRange extend(TimeRange other) {
|
||||
final TimeRange preResult = this.extend(other.from());
|
||||
final TimeRange result;
|
||||
if (preResult.to().before(other.to())) {
|
||||
result = new TimeRangeImpl(preResult.from(), other.to());
|
||||
} else {
|
||||
result = preResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDuration() {
|
||||
return from().until(to());
|
||||
|
||||
Reference in New Issue
Block a user