From 1cd701132f08d5ce8cd971292a09a4dadae08c0e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 9 Jan 2020 12:53:39 +0100 Subject: [PATCH] store the GPS fix received last for each device in the metadata collection --- .../domain/persistence/impl/FieldNames.java | 2 +- .../impl/MongoSensorFixStoreImpl.java | 90 ++++++++++--------- .../test/impl/TrackedRaceLoadsFixesTest.java | 16 ++-- .../domain/common/test/TimeRangeTest.java | 26 ++++++ .../racelog/tracking/EmptySensorFixStore.java | 2 +- .../racelog/tracking/SensorFixStore.java | 8 +- .../test/UDPExpeditionReceiverTest.java | 8 +- .../gwt/ui/server/SailingServiceImpl.java | 2 +- .../api/CourseConfigurationResource.java | 2 +- .../jaxrs/api/TrackingDeviceStatus.java | 2 +- ...CourseAndMarkConfigurationFactoryImpl.java | 2 +- .../impl/DomainObjectFactoryImpl.java | 43 ++++----- .../src/com/sap/sse/common/TimeRange.java | 19 ++++ .../sap/sse/common/impl/TimeRangeImpl.java | 42 +++++++++ 14 files changed, 185 insertions(+), 79 deletions(-) diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java index 77a99cb442b..352a3c8cc9d 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java @@ -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, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java index 40e4f0146b6..dbefb4e15c9 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreImpl.java @@ -195,37 +195,28 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore { final Object dbDeviceId = storeDeviceId(deviceServiceFinder, device); final int nrOfTotalFixes = Util.size(fixes); final ArrayList 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 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 Document storeFixToDocument(Document entry, FixT fix) throws TransformationException { + String type = fix.getClass().getName(); + FixMongoHandler mongoHandler = findService(type); + Object fixObject = mongoHandler.transformForth(fix); + entry.append(FieldNames.GPSFIX_TYPE.name(), type).append(FieldNames.GPSFIX.name(), fixObject); + return entry; + } + @Override public 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 Map getLastFix(Iterable forDevices) + public Map getFixLastReceived(Iterable forDevices) throws TransformationException, NoCorrespondingServiceRegisteredException { Map 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 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; diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java index 1d0241dbf68..ba0df09de3b 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceLoadsFixesTest.java @@ -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 lastFixes = store.getLastFix(Collections.singleton(device)); + final Map 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 lastFixes2 = store.getLastFix(Collections.singleton(device)); + final Map 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 lastFixes3 = store.getLastFix(Collections.singleton(device)); + final Map 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 lastFixes4 = store.getLastFix(Arrays.asList(device, device2)); + final Map 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 } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/TimeRangeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/TimeRangeTest.java index 663fbf30e22..695b59ffb6c 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/TimeRangeTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/TimeRangeTest.java @@ -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); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptySensorFixStore.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptySensorFixStore.java index 44eec2be528..d12ed135c48 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptySensorFixStore.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptySensorFixStore.java @@ -62,7 +62,7 @@ public enum EmptySensorFixStore implements SensorFixStore { } @Override - public Map getLastFix(Iterable forDevices) { + public Map getFixLastReceived(Iterable forDevices) { return null; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/SensorFixStore.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/SensorFixStore.java index 0d8a7622248..a7d7f1238b3 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/SensorFixStore.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/SensorFixStore.java @@ -101,7 +101,13 @@ public interface SensorFixStore { long getNumberOfFixes(DeviceIdentifier device) throws TransformationException, NoCorrespondingServiceRegisteredException; - Map getLastFix(Iterable 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. + */ + Map getFixLastReceived(Iterable forDevices) + throws TransformationException, NoCorrespondingServiceRegisteredException; /** * Loads the oldest fix for the given device in the specified {@link TimeRange}. diff --git a/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java b/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java index 1e3fed860af..31d9a4ffa43 100755 --- a/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java +++ b/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java @@ -186,7 +186,7 @@ public class UDPExpeditionReceiverTest { } @Override - public Map getLastFix(Iterable forDevices) + public Map getFixLastReceived(Iterable forDevices) throws TransformationException, NoCorrespondingServiceRegisteredException { final Map result = new HashMap<>(); for (final Entry> 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); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 834c648b192..74c47a52aac 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -7111,7 +7111,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet } private DeviceMappingDTO convertToDeviceMappingDTO(DeviceMapping mapping) throws TransformationException { - final Map lastFixes = getService().getSensorFixStore().getLastFix(Collections.singleton(mapping.getDevice())); + final Map lastFixes = getService().getSensorFixStore().getFixLastReceived(Collections.singleton(mapping.getDevice())); final Timed lastFix; if (lastFixes != null && lastFixes.containsKey(mapping.getDevice())) { lastFix = lastFixes.get(mapping.getDevice()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CourseConfigurationResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CourseConfigurationResource.java index 77e3d42d670..8caa58ce070 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CourseConfigurationResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CourseConfigurationResource.java @@ -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 lastFix = getService().getSensorFixStore().getLastFix(Collections.singleton(deviceIdentifier)); + final Map lastFix = getService().getSensorFixStore().getFixLastReceived(Collections.singleton(deviceIdentifier)); final Timed t = lastFix.get(deviceIdentifier); if (t instanceof GPSFix) { lastPosition = ((GPSFix) t).getPosition(); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java index 7fe01e9c867..5d63155eb12 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java @@ -34,7 +34,7 @@ public final class TrackingDeviceStatus { public static TrackingDeviceStatus calculateDeviceStatus(DeviceIdentifier deviceIdentifier, RacingEventService service) { try { - Map lastFixMap = service.getSensorFixStore().getLastFix(Collections.singletonList(deviceIdentifier)); + Map 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); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/CourseAndMarkConfigurationFactoryImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/CourseAndMarkConfigurationFactoryImpl.java index 127c591d9a9..c234d271a23 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/CourseAndMarkConfigurationFactoryImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/CourseAndMarkConfigurationFactoryImpl.java @@ -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 lastFix = sensorFixStore.getLastFix(Collections.singleton(identifier)); + final Map lastFix = sensorFixStore.getFixLastReceived(Collections.singleton(identifier)); final Timed t = lastFix.get(identifier); if (t instanceof GPSFix) { lastPosition = ((GPSFix) t).getPosition(); diff --git a/java/com.sap.sailing.shared.persistence/src/com/sap/sailing/shared/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.shared.persistence/src/com/sap/sailing/shared/persistence/impl/DomainObjectFactoryImpl.java index 46a747ff27f..16428cda2bf 100644 --- a/java/com.sap.sailing.shared.persistence/src/com/sap/sailing/shared/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.shared.persistence/src/com/sap/sailing/shared/persistence/impl/DomainObjectFactoryImpl.java @@ -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 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; } } diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/TimeRange.java b/java/com.sap.sse.common/src/com/sap/sse/common/TimeRange.java index 2d32ff84af8..f03f5fca2aa 100755 --- a/java/com.sap.sse.common/src/com/sap/sse/common/TimeRange.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/TimeRange.java @@ -63,9 +63,13 @@ public interface TimeRange extends Comparable, 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, 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); } \ No newline at end of file diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/impl/TimeRangeImpl.java b/java/com.sap.sse.common/src/com/sap/sse/common/impl/TimeRangeImpl.java index fd12dc129e8..a7d238abefa 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/impl/TimeRangeImpl.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/impl/TimeRangeImpl.java @@ -22,6 +22,10 @@ public class TimeRangeImpl extends Util.Pair 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 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 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());