Add rest geo evidence and boundary odometers
This commit is contained in:
parent
4535f620fc
commit
f1f36e2204
|
|
@ -360,6 +360,10 @@ public class EventHubProperties {
|
|||
private int operatingSplitIdleHours = 7;
|
||||
private int significantDrivingMinutes = 3;
|
||||
private int minimumRestPeriodMinutes = 720;
|
||||
private int restCandidateGeoLookbackMinutes = 180;
|
||||
private int restCandidateGeoLookaheadMinutes = 180;
|
||||
private int restCandidateGeoStationaryMaxMeters = 500;
|
||||
private int restCandidateGeoMinorMovementMaxMeters = 2000;
|
||||
private int mergeGapSeconds = 0;
|
||||
private int gapDetectionToleranceSeconds = 0;
|
||||
|
||||
|
|
@ -397,6 +401,39 @@ public class EventHubProperties {
|
|||
this.minimumRestPeriodMinutes = Math.max(1, minimumRestPeriodMinutes);
|
||||
}
|
||||
|
||||
public int getRestCandidateGeoLookbackMinutes() {
|
||||
return restCandidateGeoLookbackMinutes;
|
||||
}
|
||||
|
||||
public void setRestCandidateGeoLookbackMinutes(int restCandidateGeoLookbackMinutes) {
|
||||
this.restCandidateGeoLookbackMinutes = Math.max(1, restCandidateGeoLookbackMinutes);
|
||||
}
|
||||
|
||||
public int getRestCandidateGeoLookaheadMinutes() {
|
||||
return restCandidateGeoLookaheadMinutes;
|
||||
}
|
||||
|
||||
public void setRestCandidateGeoLookaheadMinutes(int restCandidateGeoLookaheadMinutes) {
|
||||
this.restCandidateGeoLookaheadMinutes = Math.max(1, restCandidateGeoLookaheadMinutes);
|
||||
}
|
||||
|
||||
public int getRestCandidateGeoStationaryMaxMeters() {
|
||||
return restCandidateGeoStationaryMaxMeters;
|
||||
}
|
||||
|
||||
public void setRestCandidateGeoStationaryMaxMeters(int restCandidateGeoStationaryMaxMeters) {
|
||||
this.restCandidateGeoStationaryMaxMeters = Math.max(0, restCandidateGeoStationaryMaxMeters);
|
||||
}
|
||||
|
||||
public int getRestCandidateGeoMinorMovementMaxMeters() {
|
||||
return restCandidateGeoMinorMovementMaxMeters;
|
||||
}
|
||||
|
||||
public void setRestCandidateGeoMinorMovementMaxMeters(int restCandidateGeoMinorMovementMaxMeters) {
|
||||
this.restCandidateGeoMinorMovementMaxMeters =
|
||||
Math.max(this.restCandidateGeoStationaryMaxMeters, restCandidateGeoMinorMovementMaxMeters);
|
||||
}
|
||||
|
||||
public int getMergeGapSeconds() {
|
||||
return mergeGapSeconds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInte
|
|||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleTripIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperSupportGeoEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import java.time.OffsetDateTime;
|
||||
|
|
@ -32,6 +33,7 @@ public record TachographEsperDriverProcessingResultDto(
|
|||
int potentialInVehicleTripIntervalCount,
|
||||
int vehicleUsageIntervalCount,
|
||||
int vuCardAbsentIntervalCount,
|
||||
int supportGeoEventCount,
|
||||
List<TachographEsperActivityIntervalEvent> activityIntervals,
|
||||
List<TachographEsperActivityIntervalEvent> drivingIntervals,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||
|
|
@ -44,6 +46,7 @@ public record TachographEsperDriverProcessingResultDto(
|
|||
List<TachographEsperPotentialInVehicleTripIntervalEvent> potentialInVehicleTripIntervals,
|
||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||
List<TachographEsperSupportGeoEvent> supportGeoEvents,
|
||||
List<String> notes
|
||||
) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,26 @@ public record TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
|||
String previousRegistrationKey,
|
||||
String nextRegistrationKey,
|
||||
String previousVehicleKey,
|
||||
String nextVehicleKey
|
||||
String nextVehicleKey,
|
||||
Long beginBoundaryOdometerKm,
|
||||
Long endBoundaryOdometerKm,
|
||||
String beginGeoEventId,
|
||||
String beginGeoEventDomain,
|
||||
OffsetDateTime beginGeoOccurredAt,
|
||||
Double beginLatitude,
|
||||
Double beginLongitude,
|
||||
Long beginGeoDistanceSeconds,
|
||||
Long beginGeoOdometerKm,
|
||||
String endGeoEventId,
|
||||
String endGeoEventDomain,
|
||||
OffsetDateTime endGeoOccurredAt,
|
||||
Double endLatitude,
|
||||
Double endLongitude,
|
||||
Long endGeoDistanceSeconds,
|
||||
Long endGeoOdometerKm,
|
||||
Long geoEvidenceMovementMeters,
|
||||
String geoEvidenceMovementCategory,
|
||||
TachographEsperGeoEvidenceEvent beginGeoEvent,
|
||||
TachographEsperGeoEvidenceEvent endGeoEvent
|
||||
) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public record TachographEsperGeoEvidenceEvent(
|
||||
String eventId,
|
||||
String eventDomain,
|
||||
OffsetDateTime occurredAt,
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
Long distanceSeconds,
|
||||
Long odometerKm
|
||||
) {
|
||||
}
|
||||
|
|
@ -18,6 +18,26 @@ public record TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
|||
String previousRegistrationKey,
|
||||
String nextRegistrationKey,
|
||||
String previousVehicleKey,
|
||||
String nextVehicleKey
|
||||
String nextVehicleKey,
|
||||
Long beginBoundaryOdometerKm,
|
||||
Long endBoundaryOdometerKm,
|
||||
String beginGeoEventId,
|
||||
String beginGeoEventDomain,
|
||||
OffsetDateTime beginGeoOccurredAt,
|
||||
Double beginLatitude,
|
||||
Double beginLongitude,
|
||||
Long beginGeoDistanceSeconds,
|
||||
Long beginGeoOdometerKm,
|
||||
String endGeoEventId,
|
||||
String endGeoEventDomain,
|
||||
OffsetDateTime endGeoOccurredAt,
|
||||
Double endLatitude,
|
||||
Double endLongitude,
|
||||
Long endGeoDistanceSeconds,
|
||||
Long endGeoOdometerKm,
|
||||
Long geoEvidenceMovementMeters,
|
||||
String geoEvidenceMovementCategory,
|
||||
TachographEsperGeoEvidenceEvent beginGeoEvent,
|
||||
TachographEsperGeoEvidenceEvent endGeoEvent
|
||||
) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,26 @@ public record TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
|||
String previousRegistrationKey,
|
||||
String nextRegistrationKey,
|
||||
String previousVehicleKey,
|
||||
String nextVehicleKey
|
||||
String nextVehicleKey,
|
||||
Long beginBoundaryOdometerKm,
|
||||
Long endBoundaryOdometerKm,
|
||||
String beginGeoEventId,
|
||||
String beginGeoEventDomain,
|
||||
OffsetDateTime beginGeoOccurredAt,
|
||||
Double beginLatitude,
|
||||
Double beginLongitude,
|
||||
Long beginGeoDistanceSeconds,
|
||||
Long beginGeoOdometerKm,
|
||||
String endGeoEventId,
|
||||
String endGeoEventDomain,
|
||||
OffsetDateTime endGeoOccurredAt,
|
||||
Double endLatitude,
|
||||
Double endLongitude,
|
||||
Long endGeoDistanceSeconds,
|
||||
Long endGeoOdometerKm,
|
||||
Long geoEvidenceMovementMeters,
|
||||
String geoEvidenceMovementCategory,
|
||||
TachographEsperGeoEvidenceEvent beginGeoEvent,
|
||||
TachographEsperGeoEvidenceEvent endGeoEvent
|
||||
) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public record TachographEsperSupportGeoEvent(
|
||||
String eventId,
|
||||
String driverKey,
|
||||
OffsetDateTime occurredAt,
|
||||
String eventDomain,
|
||||
String eventType,
|
||||
String eventLifecycle,
|
||||
String registrationKey,
|
||||
String vehicleKey,
|
||||
String country,
|
||||
String region,
|
||||
String countryFrom,
|
||||
String countryTo,
|
||||
String operation,
|
||||
BigDecimal latitude,
|
||||
BigDecimal longitude,
|
||||
Long odometerKm,
|
||||
String rawRecordPath
|
||||
) {
|
||||
}
|
||||
|
|
@ -1006,7 +1006,27 @@ public class DriverTimelineBuilder {
|
|||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
(String) event.get("nextVehicleKey"),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"UNKNOWN",
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,16 @@ import com.espertech.esper.runtime.client.EPDeployException;
|
|||
import com.espertech.esper.runtime.client.EPDeployment;
|
||||
import com.espertech.esper.runtime.client.EPRuntime;
|
||||
import com.espertech.esper.runtime.client.EPRuntimeProvider;
|
||||
import at.procon.eventhub.config.EventHubProperties;
|
||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperGeoEvidenceEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleTripIntervalEvent;
|
||||
|
|
@ -47,9 +50,14 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
loadResource("esper/tachograph-driving-derived-projection-bundle.epl");
|
||||
|
||||
private final DriverTimelineBuilder driverTimelineBuilder;
|
||||
private final EventHubProperties properties;
|
||||
|
||||
public DriverTimelineReusableProjectionBuilder(DriverTimelineBuilder driverTimelineBuilder) {
|
||||
public DriverTimelineReusableProjectionBuilder(
|
||||
DriverTimelineBuilder driverTimelineBuilder,
|
||||
EventHubProperties properties
|
||||
) {
|
||||
this.driverTimelineBuilder = driverTimelineBuilder;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public TachographEsperDrivingDerivedProjectionBundle buildEsperDrivingDerivedProjectionBundle(
|
||||
|
|
@ -83,6 +91,7 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
driverKey,
|
||||
timeline.activityIntervals(),
|
||||
timeline.vehicleUsageIntervals(),
|
||||
timeline.supportEvents(),
|
||||
significantDrivingMinutes,
|
||||
minimumRestPeriodMinutes
|
||||
);
|
||||
|
|
@ -93,6 +102,7 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
String driverKey,
|
||||
List<ResolvedActivityInterval> activityIntervals,
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
|
||||
List<ExtractedSupportEvent> supportEvents,
|
||||
int significantDrivingMinutes,
|
||||
int minimumRestPeriodMinutes
|
||||
) {
|
||||
|
|
@ -121,6 +131,10 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
"TachographVehicleUsageIntervalInputEvent",
|
||||
vehicleUsageIntervalInputDefinition()
|
||||
);
|
||||
configuration.getCommon().addEventType(
|
||||
"TachographSupportGeoEvidenceInputEvent",
|
||||
supportGeoEvidenceInputDefinition()
|
||||
);
|
||||
},
|
||||
renderDrivingDerivedProjectionBundleEpl(significantDrivingMinutes, minimumRestPeriodMinutes),
|
||||
Map.of(
|
||||
|
|
@ -135,6 +149,17 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
"potentialInVehicleTripIntervals", newData -> collectPotentialInVehicleTripIntervalEvents(newData, potentialInVehicleTripIntervals)
|
||||
),
|
||||
runtime -> {
|
||||
if (supportEvents != null) {
|
||||
for (ExtractedSupportEvent supportEvent : supportEvents) {
|
||||
Map<String, Object> supportGeoEvidence = toSupportGeoEvidenceInputMap(sessionId, supportEvent);
|
||||
if (supportGeoEvidence != null) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
supportGeoEvidence,
|
||||
"TachographSupportGeoEvidenceInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vehicleUsageIntervals != null) {
|
||||
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
|
|
@ -260,6 +285,23 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
return definition;
|
||||
}
|
||||
|
||||
private Map<String, Object> supportGeoEvidenceInputDefinition() {
|
||||
Map<String, Object> definition = new LinkedHashMap<>();
|
||||
definition.put("sessionId", UUID.class);
|
||||
definition.put("driverKey", String.class);
|
||||
definition.put("eventId", String.class);
|
||||
definition.put("eventDomain", String.class);
|
||||
definition.put("occurredAt", OffsetDateTime.class);
|
||||
definition.put("occurredAtEpochSecond", long.class);
|
||||
definition.put("registrationKey", String.class);
|
||||
definition.put("vehicleKey", String.class);
|
||||
definition.put("latitude", Double.class);
|
||||
definition.put("longitude", Double.class);
|
||||
definition.put("odometerKm", Long.class);
|
||||
definition.put("priority", int.class);
|
||||
return definition;
|
||||
}
|
||||
|
||||
private Map<String, Object> toActivityIntervalInputMap(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
|
|
@ -311,6 +353,50 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
return event;
|
||||
}
|
||||
|
||||
private Map<String, Object> toSupportGeoEvidenceInputMap(
|
||||
UUID sessionId,
|
||||
ExtractedSupportEvent supportEvent
|
||||
) {
|
||||
if (supportEvent == null
|
||||
|| supportEvent.driverKey() == null
|
||||
|| supportEvent.occurredAt() == null
|
||||
|| supportEvent.latitude() == null
|
||||
|| supportEvent.longitude() == null) {
|
||||
return null;
|
||||
}
|
||||
int priority = supportGeoPriority(supportEvent.eventDomain());
|
||||
if (priority <= 0) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> event = new LinkedHashMap<>();
|
||||
event.put("sessionId", sessionId);
|
||||
event.put("driverKey", supportEvent.driverKey());
|
||||
event.put("eventId", supportEvent.eventId());
|
||||
event.put("eventDomain", supportEvent.eventDomain());
|
||||
event.put("occurredAt", supportEvent.occurredAt());
|
||||
event.put("occurredAtEpochSecond", supportEvent.occurredAt().toEpochSecond());
|
||||
event.put("registrationKey", supportEvent.registrationKey());
|
||||
event.put("vehicleKey", supportEvent.vehicleKey());
|
||||
event.put("latitude", supportEvent.latitude().doubleValue());
|
||||
event.put("longitude", supportEvent.longitude().doubleValue());
|
||||
event.put("odometerKm", supportEvent.odometerKm());
|
||||
event.put("priority", priority);
|
||||
return event;
|
||||
}
|
||||
|
||||
private int supportGeoPriority(String eventDomain) {
|
||||
if (eventDomain == null || eventDomain.isBlank()) {
|
||||
return 0;
|
||||
}
|
||||
return switch (eventDomain.trim().toUpperCase()) {
|
||||
case "POSITION" -> 500;
|
||||
case "PLACE" -> 400;
|
||||
case "BORDER_CROSSING" -> 300;
|
||||
case "LOAD_UNLOAD" -> 250;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private String firstSourceIntervalId(ResolvedActivityInterval interval) {
|
||||
return interval.sourceIntervalIds().isEmpty() ? interval.intervalId() : interval.sourceIntervalIds().get(0);
|
||||
}
|
||||
|
|
@ -393,6 +479,24 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
for (EventBean event : newData) {
|
||||
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||
TachographEsperGeoEvidenceEvent beginGeoEvent = geoEvidenceEvent(
|
||||
(String) event.get("beginGeoEventId"),
|
||||
(String) event.get("beginGeoEventDomain"),
|
||||
offsetDateTime(event.get("beginGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("beginLatitude"),
|
||||
(Double) event.get("beginLongitude"),
|
||||
longOrNull(event.get("beginGeoDistanceSeconds")),
|
||||
longOrNull(event.get("beginGeoOdometerKm"))
|
||||
);
|
||||
TachographEsperGeoEvidenceEvent endGeoEvent = geoEvidenceEvent(
|
||||
(String) event.get("endGeoEventId"),
|
||||
(String) event.get("endGeoEventDomain"),
|
||||
offsetDateTime(event.get("endGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("endLatitude"),
|
||||
(Double) event.get("endLongitude"),
|
||||
longOrNull(event.get("endGeoDistanceSeconds")),
|
||||
longOrNull(event.get("endGeoOdometerKm"))
|
||||
);
|
||||
target.add(new TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
|
|
@ -408,7 +512,27 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
(String) event.get("nextVehicleKey"),
|
||||
longOrNull(event.get("beginBoundaryOdometerKm")),
|
||||
longOrNull(event.get("endBoundaryOdometerKm")),
|
||||
(String) event.get("beginGeoEventId"),
|
||||
(String) event.get("beginGeoEventDomain"),
|
||||
offsetDateTime(event.get("beginGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("beginLatitude"),
|
||||
(Double) event.get("beginLongitude"),
|
||||
longOrNull(event.get("beginGeoDistanceSeconds")),
|
||||
longOrNull(event.get("beginGeoOdometerKm")),
|
||||
(String) event.get("endGeoEventId"),
|
||||
(String) event.get("endGeoEventDomain"),
|
||||
offsetDateTime(event.get("endGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("endLatitude"),
|
||||
(Double) event.get("endLongitude"),
|
||||
longOrNull(event.get("endGeoDistanceSeconds")),
|
||||
longOrNull(event.get("endGeoOdometerKm")),
|
||||
longOrNull(event.get("geoEvidenceMovementMeters")),
|
||||
(String) event.get("geoEvidenceMovementCategory"),
|
||||
beginGeoEvent,
|
||||
endGeoEvent
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -423,6 +547,24 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
for (EventBean event : newData) {
|
||||
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||
TachographEsperGeoEvidenceEvent beginGeoEvent = geoEvidenceEvent(
|
||||
(String) event.get("beginGeoEventId"),
|
||||
(String) event.get("beginGeoEventDomain"),
|
||||
offsetDateTime(event.get("beginGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("beginLatitude"),
|
||||
(Double) event.get("beginLongitude"),
|
||||
longOrNull(event.get("beginGeoDistanceSeconds")),
|
||||
longOrNull(event.get("beginGeoOdometerKm"))
|
||||
);
|
||||
TachographEsperGeoEvidenceEvent endGeoEvent = geoEvidenceEvent(
|
||||
(String) event.get("endGeoEventId"),
|
||||
(String) event.get("endGeoEventDomain"),
|
||||
offsetDateTime(event.get("endGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("endLatitude"),
|
||||
(Double) event.get("endLongitude"),
|
||||
longOrNull(event.get("endGeoDistanceSeconds")),
|
||||
longOrNull(event.get("endGeoOdometerKm"))
|
||||
);
|
||||
target.add(new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
|
|
@ -438,7 +580,27 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
(String) event.get("nextVehicleKey"),
|
||||
longOrNull(event.get("beginBoundaryOdometerKm")),
|
||||
longOrNull(event.get("endBoundaryOdometerKm")),
|
||||
(String) event.get("beginGeoEventId"),
|
||||
(String) event.get("beginGeoEventDomain"),
|
||||
offsetDateTime(event.get("beginGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("beginLatitude"),
|
||||
(Double) event.get("beginLongitude"),
|
||||
longOrNull(event.get("beginGeoDistanceSeconds")),
|
||||
longOrNull(event.get("beginGeoOdometerKm")),
|
||||
(String) event.get("endGeoEventId"),
|
||||
(String) event.get("endGeoEventDomain"),
|
||||
offsetDateTime(event.get("endGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("endLatitude"),
|
||||
(Double) event.get("endLongitude"),
|
||||
longOrNull(event.get("endGeoDistanceSeconds")),
|
||||
longOrNull(event.get("endGeoOdometerKm")),
|
||||
longOrNull(event.get("geoEvidenceMovementMeters")),
|
||||
(String) event.get("geoEvidenceMovementCategory"),
|
||||
beginGeoEvent,
|
||||
endGeoEvent
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -453,6 +615,24 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
for (EventBean event : newData) {
|
||||
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||
TachographEsperGeoEvidenceEvent beginGeoEvent = geoEvidenceEvent(
|
||||
(String) event.get("beginGeoEventId"),
|
||||
(String) event.get("beginGeoEventDomain"),
|
||||
offsetDateTime(event.get("beginGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("beginLatitude"),
|
||||
(Double) event.get("beginLongitude"),
|
||||
longOrNull(event.get("beginGeoDistanceSeconds")),
|
||||
longOrNull(event.get("beginGeoOdometerKm"))
|
||||
);
|
||||
TachographEsperGeoEvidenceEvent endGeoEvent = geoEvidenceEvent(
|
||||
(String) event.get("endGeoEventId"),
|
||||
(String) event.get("endGeoEventDomain"),
|
||||
offsetDateTime(event.get("endGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("endLatitude"),
|
||||
(Double) event.get("endLongitude"),
|
||||
longOrNull(event.get("endGeoDistanceSeconds")),
|
||||
longOrNull(event.get("endGeoOdometerKm"))
|
||||
);
|
||||
target.add(new TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
|
|
@ -468,7 +648,27 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
(String) event.get("nextVehicleKey"),
|
||||
longOrNull(event.get("beginBoundaryOdometerKm")),
|
||||
longOrNull(event.get("endBoundaryOdometerKm")),
|
||||
(String) event.get("beginGeoEventId"),
|
||||
(String) event.get("beginGeoEventDomain"),
|
||||
offsetDateTime(event.get("beginGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("beginLatitude"),
|
||||
(Double) event.get("beginLongitude"),
|
||||
longOrNull(event.get("beginGeoDistanceSeconds")),
|
||||
longOrNull(event.get("beginGeoOdometerKm")),
|
||||
(String) event.get("endGeoEventId"),
|
||||
(String) event.get("endGeoEventDomain"),
|
||||
offsetDateTime(event.get("endGeoOccurredAtEpochSecond")),
|
||||
(Double) event.get("endLatitude"),
|
||||
(Double) event.get("endLongitude"),
|
||||
longOrNull(event.get("endGeoDistanceSeconds")),
|
||||
longOrNull(event.get("endGeoOdometerKm")),
|
||||
longOrNull(event.get("geoEvidenceMovementMeters")),
|
||||
(String) event.get("geoEvidenceMovementCategory"),
|
||||
beginGeoEvent,
|
||||
endGeoEvent
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -567,6 +767,65 @@ public class DriverTimelineReusableProjectionBuilder {
|
|||
.replace(
|
||||
"${MINIMUM_REST_PERIOD_THRESHOLD_SECONDS}",
|
||||
Long.toString(Math.max(1, minimumRestPeriodMinutes) * 60L)
|
||||
)
|
||||
.replace(
|
||||
"${REST_GEO_LOOKBACK_SECONDS}",
|
||||
Long.toString(Math.max(1, properties.getTachographFileSession().getProcessing().getRestCandidateGeoLookbackMinutes()) * 60L)
|
||||
)
|
||||
.replace(
|
||||
"${REST_GEO_LOOKAHEAD_SECONDS}",
|
||||
Long.toString(Math.max(1, properties.getTachographFileSession().getProcessing().getRestCandidateGeoLookaheadMinutes()) * 60L)
|
||||
)
|
||||
.replace(
|
||||
"${REST_GEO_STATIONARY_MAX_METERS}",
|
||||
Integer.toString(Math.max(0, properties.getTachographFileSession().getProcessing().getRestCandidateGeoStationaryMaxMeters()))
|
||||
)
|
||||
.replace(
|
||||
"${REST_GEO_MINOR_MOVEMENT_MAX_METERS}",
|
||||
Integer.toString(Math.max(
|
||||
properties.getTachographFileSession().getProcessing().getRestCandidateGeoStationaryMaxMeters(),
|
||||
properties.getTachographFileSession().getProcessing().getRestCandidateGeoMinorMovementMaxMeters()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
private OffsetDateTime offsetDateTime(Object epochSecond) {
|
||||
if (!(epochSecond instanceof Long value)) {
|
||||
return null;
|
||||
}
|
||||
return OffsetDateTime.ofInstant(Instant.ofEpochSecond(value), ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private Long longOrNull(Object value) {
|
||||
return value instanceof Long number ? number : null;
|
||||
}
|
||||
|
||||
private TachographEsperGeoEvidenceEvent geoEvidenceEvent(
|
||||
String eventId,
|
||||
String eventDomain,
|
||||
OffsetDateTime occurredAt,
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
Long distanceSeconds,
|
||||
Long odometerKm
|
||||
) {
|
||||
if (eventId == null
|
||||
&& eventDomain == null
|
||||
&& occurredAt == null
|
||||
&& latitude == null
|
||||
&& longitude == null
|
||||
&& distanceSeconds == null
|
||||
&& odometerKm == null) {
|
||||
return null;
|
||||
}
|
||||
return new TachographEsperGeoEvidenceEvent(
|
||||
eventId,
|
||||
eventDomain,
|
||||
occurredAt,
|
||||
latitude,
|
||||
longitude,
|
||||
distanceSeconds,
|
||||
odometerKm
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcess
|
|||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.PeriodizedDriverActivityInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.ProcessedDrivingInterruption;
|
||||
import at.procon.eventhub.tachographfilesession.model.ProcessedOperatingPeriod;
|
||||
|
|
@ -17,9 +18,11 @@ import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeekly
|
|||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperGeoEvidenceEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleTripIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperSupportGeoEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import java.time.Duration;
|
||||
|
|
@ -256,6 +259,12 @@ public class TachographFileSessionProcessingService {
|
|||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperSupportGeoEvent> supportGeoEvents = clipEsperSupportGeoEvents(
|
||||
timeline.supportEvents(),
|
||||
driverKey,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
|
||||
return new TachographEsperDriverProcessingResultDto(
|
||||
sessionId,
|
||||
|
|
@ -277,8 +286,9 @@ public class TachographFileSessionProcessingService {
|
|||
potentialInVehicleTripIntervals.size(),
|
||||
vehicleUsageIntervals.size(),
|
||||
vuCardAbsentIntervals.size(),
|
||||
List.of()/*activityIntervals*/,
|
||||
List.of()/*drivingIntervals*/,
|
||||
supportGeoEvents.size(),
|
||||
activityIntervals,
|
||||
drivingIntervals,
|
||||
drivingInterruptionIntervals,
|
||||
drivingInterruptionVehicleChangeIntervals,
|
||||
dailyWeeklyRestCandidateIntervals,
|
||||
|
|
@ -289,6 +299,7 @@ public class TachographFileSessionProcessingService {
|
|||
potentialInVehicleTripIntervals,
|
||||
vehicleUsageIntervals,
|
||||
vuCardAbsentIntervals,
|
||||
supportGeoEvents,
|
||||
esperProjectionNotes()
|
||||
);
|
||||
}
|
||||
|
|
@ -388,6 +399,45 @@ public class TachographFileSessionProcessingService {
|
|||
.toList();
|
||||
}
|
||||
|
||||
private List<TachographEsperSupportGeoEvent> clipEsperSupportGeoEvents(
|
||||
List<ExtractedSupportEvent> supportEvents,
|
||||
String driverKey,
|
||||
OffsetDateTime requestedFrom,
|
||||
OffsetDateTime requestedTo
|
||||
) {
|
||||
if (supportEvents == null || supportEvents.isEmpty() || requestedFrom == null || requestedTo == null) {
|
||||
return List.of();
|
||||
}
|
||||
return supportEvents.stream()
|
||||
.filter(event -> Objects.equals(driverKey, event.driverKey()))
|
||||
.filter(event -> event.occurredAt() != null)
|
||||
.filter(event -> event.latitude() != null && event.longitude() != null)
|
||||
.filter(event -> !event.occurredAt().isBefore(requestedFrom) && !event.occurredAt().isAfter(requestedTo))
|
||||
.map(event -> new TachographEsperSupportGeoEvent(
|
||||
event.eventId(),
|
||||
event.driverKey(),
|
||||
event.occurredAt(),
|
||||
event.eventDomain(),
|
||||
event.eventType(),
|
||||
event.eventLifecycle(),
|
||||
event.registrationKey(),
|
||||
event.vehicleKey(),
|
||||
event.country(),
|
||||
event.region(),
|
||||
event.countryFrom(),
|
||||
event.countryTo(),
|
||||
event.operation(),
|
||||
event.latitude(),
|
||||
event.longitude(),
|
||||
event.odometerKm(),
|
||||
event.rawRecordPath()
|
||||
))
|
||||
.sorted(Comparator.comparing(TachographEsperSupportGeoEvent::occurredAt)
|
||||
.thenComparing(TachographEsperSupportGeoEvent::eventDomain, Comparator.nullsLast(String::compareTo))
|
||||
.thenComparing(TachographEsperSupportGeoEvent::eventId, Comparator.nullsLast(String::compareTo)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<TachographEsperDrivingInterruptionIntervalEvent> clipEsperDrivingInterruptionIntervalEvents(
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> intervals,
|
||||
OffsetDateTime requestedFrom,
|
||||
|
|
@ -495,6 +545,8 @@ public class TachographFileSessionProcessingService {
|
|||
double unknownCoveragePercent = durationSeconds == 0L
|
||||
? 0.0d
|
||||
: (unknownDurationSeconds * 100.0d) / durationSeconds;
|
||||
boolean beginBoundaryChanged = !start.equals(interval.startedAt());
|
||||
boolean endBoundaryChanged = !end.equals(interval.endedAt());
|
||||
return new TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
|
|
@ -510,7 +562,25 @@ public class TachographFileSessionProcessingService {
|
|||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
interval.nextVehicleKey(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoEventId(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoEventDomain(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoOccurredAt(),
|
||||
beginBoundaryChanged ? null : interval.beginLatitude(),
|
||||
beginBoundaryChanged ? null : interval.beginLongitude(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoDistanceSeconds(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoOdometerKm(),
|
||||
endBoundaryChanged ? null : interval.endGeoEventId(),
|
||||
endBoundaryChanged ? null : interval.endGeoEventDomain(),
|
||||
endBoundaryChanged ? null : interval.endGeoOccurredAt(),
|
||||
endBoundaryChanged ? null : interval.endLatitude(),
|
||||
endBoundaryChanged ? null : interval.endLongitude(),
|
||||
endBoundaryChanged ? null : interval.endGeoDistanceSeconds(),
|
||||
endBoundaryChanged ? null : interval.endGeoOdometerKm(),
|
||||
beginBoundaryChanged || endBoundaryChanged ? null : interval.geoEvidenceMovementMeters(),
|
||||
beginBoundaryChanged || endBoundaryChanged ? "UNKNOWN" : interval.geoEvidenceMovementCategory(),
|
||||
beginBoundaryChanged ? null : geoEvidenceEvent(interval.beginGeoEventId(), interval.beginGeoEventDomain(), interval.beginGeoOccurredAt(), interval.beginLatitude(), interval.beginLongitude(), interval.beginGeoDistanceSeconds(), interval.beginGeoOdometerKm()),
|
||||
endBoundaryChanged ? null : geoEvidenceEvent(interval.endGeoEventId(), interval.endGeoEventDomain(), interval.endGeoOccurredAt(), interval.endLatitude(), interval.endLongitude(), interval.endGeoDistanceSeconds(), interval.endGeoOdometerKm())
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
|
|
@ -556,6 +626,8 @@ public class TachographFileSessionProcessingService {
|
|||
double unknownCoveragePercent = durationSeconds == 0L
|
||||
? 0.0d
|
||||
: (unknownDurationSeconds * 100.0d) / durationSeconds;
|
||||
boolean beginBoundaryChanged = !start.equals(interval.startedAt());
|
||||
boolean endBoundaryChanged = !end.equals(interval.endedAt());
|
||||
return new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
|
|
@ -571,7 +643,25 @@ public class TachographFileSessionProcessingService {
|
|||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
interval.nextVehicleKey(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoEventId(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoEventDomain(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoOccurredAt(),
|
||||
beginBoundaryChanged ? null : interval.beginLatitude(),
|
||||
beginBoundaryChanged ? null : interval.beginLongitude(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoDistanceSeconds(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoOdometerKm(),
|
||||
endBoundaryChanged ? null : interval.endGeoEventId(),
|
||||
endBoundaryChanged ? null : interval.endGeoEventDomain(),
|
||||
endBoundaryChanged ? null : interval.endGeoOccurredAt(),
|
||||
endBoundaryChanged ? null : interval.endLatitude(),
|
||||
endBoundaryChanged ? null : interval.endLongitude(),
|
||||
endBoundaryChanged ? null : interval.endGeoDistanceSeconds(),
|
||||
endBoundaryChanged ? null : interval.endGeoOdometerKm(),
|
||||
beginBoundaryChanged || endBoundaryChanged ? null : interval.geoEvidenceMovementMeters(),
|
||||
beginBoundaryChanged || endBoundaryChanged ? "UNKNOWN" : interval.geoEvidenceMovementCategory(),
|
||||
beginBoundaryChanged ? null : geoEvidenceEvent(interval.beginGeoEventId(), interval.beginGeoEventDomain(), interval.beginGeoOccurredAt(), interval.beginLatitude(), interval.beginLongitude(), interval.beginGeoDistanceSeconds(), interval.beginGeoOdometerKm()),
|
||||
endBoundaryChanged ? null : geoEvidenceEvent(interval.endGeoEventId(), interval.endGeoEventDomain(), interval.endGeoOccurredAt(), interval.endLatitude(), interval.endLongitude(), interval.endGeoDistanceSeconds(), interval.endGeoOdometerKm())
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
|
|
@ -617,6 +707,8 @@ public class TachographFileSessionProcessingService {
|
|||
double unknownCoveragePercent = durationSeconds == 0L
|
||||
? 0.0d
|
||||
: (unknownDurationSeconds * 100.0d) / durationSeconds;
|
||||
boolean beginBoundaryChanged = !start.equals(interval.startedAt());
|
||||
boolean endBoundaryChanged = !end.equals(interval.endedAt());
|
||||
return new TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
|
|
@ -632,7 +724,25 @@ public class TachographFileSessionProcessingService {
|
|||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
interval.nextVehicleKey(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoEventId(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoEventDomain(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoOccurredAt(),
|
||||
beginBoundaryChanged ? null : interval.beginLatitude(),
|
||||
beginBoundaryChanged ? null : interval.beginLongitude(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoDistanceSeconds(),
|
||||
beginBoundaryChanged ? null : interval.beginGeoOdometerKm(),
|
||||
endBoundaryChanged ? null : interval.endGeoEventId(),
|
||||
endBoundaryChanged ? null : interval.endGeoEventDomain(),
|
||||
endBoundaryChanged ? null : interval.endGeoOccurredAt(),
|
||||
endBoundaryChanged ? null : interval.endLatitude(),
|
||||
endBoundaryChanged ? null : interval.endLongitude(),
|
||||
endBoundaryChanged ? null : interval.endGeoDistanceSeconds(),
|
||||
endBoundaryChanged ? null : interval.endGeoOdometerKm(),
|
||||
beginBoundaryChanged || endBoundaryChanged ? null : interval.geoEvidenceMovementMeters(),
|
||||
beginBoundaryChanged || endBoundaryChanged ? "UNKNOWN" : interval.geoEvidenceMovementCategory(),
|
||||
beginBoundaryChanged ? null : geoEvidenceEvent(interval.beginGeoEventId(), interval.beginGeoEventDomain(), interval.beginGeoOccurredAt(), interval.beginLatitude(), interval.beginLongitude(), interval.beginGeoDistanceSeconds(), interval.beginGeoOdometerKm()),
|
||||
endBoundaryChanged ? null : geoEvidenceEvent(interval.endGeoEventId(), interval.endGeoEventDomain(), interval.endGeoOccurredAt(), interval.endLatitude(), interval.endLongitude(), interval.endGeoDistanceSeconds(), interval.endGeoOdometerKm())
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
|
|
@ -1300,6 +1410,9 @@ public class TachographFileSessionProcessingService {
|
|||
"Driving interruption vehicle-change intervals are daily/weekly rest candidates where previousRegistrationKey differs from nextRegistrationKey.",
|
||||
"Daily/weekly rest candidate intervals are driving interruption intervals longer than the configured minimum rest-period threshold.",
|
||||
"Daily/weekly rest candidate coverage intervals enrich each rest candidate with card-present and unknown-coverage metrics computed from vehicle-usage and VU card-absent overlap.",
|
||||
"Daily/weekly rest candidate coverage intervals also attach begin/end geo evidence from nearby support events for the same driver and boundary-side vehicle identity.",
|
||||
"Boundary geo evidence prefers the nearest matching POSITION event, then PLACE, BORDER_CROSSING, and LOAD_UNLOAD within the configured lookback/lookahead windows.",
|
||||
"If both begin and end geo evidence carry odometer values, geoEvidenceMovementCategory classifies the interval as STATIONARY, MINOR, MOVED, or UNKNOWN.",
|
||||
"Unclassified daily/weekly rest candidate coverage intervals are the rest candidates that are neither potential home overnight stays nor potential in-vehicle overnight stays.",
|
||||
"Potential home overnight stay intervals are vehicle-change daily/weekly rest candidate coverage intervals where VU card-absent overlap covers at least 95% of the candidate interval.",
|
||||
"Potential in-vehicle overnight stay intervals are no-change daily/weekly rest candidate coverage intervals where card-present overlap covers the candidate rest period.",
|
||||
|
|
@ -1367,6 +1480,35 @@ public class TachographFileSessionProcessingService {
|
|||
return value == null ? null : value.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private TachographEsperGeoEvidenceEvent geoEvidenceEvent(
|
||||
String eventId,
|
||||
String eventDomain,
|
||||
OffsetDateTime occurredAt,
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
Long distanceSeconds,
|
||||
Long odometerKm
|
||||
) {
|
||||
if (eventId == null
|
||||
&& eventDomain == null
|
||||
&& occurredAt == null
|
||||
&& latitude == null
|
||||
&& longitude == null
|
||||
&& distanceSeconds == null
|
||||
&& odometerKm == null) {
|
||||
return null;
|
||||
}
|
||||
return new TachographEsperGeoEvidenceEvent(
|
||||
eventId,
|
||||
eventDomain,
|
||||
occurredAt,
|
||||
latitude,
|
||||
longitude,
|
||||
distanceSeconds,
|
||||
odometerKm
|
||||
);
|
||||
}
|
||||
|
||||
private OffsetDateTime max(OffsetDateTime left, OffsetDateTime right) {
|
||||
if (left == null) {
|
||||
return right;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -24,6 +24,7 @@ import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeekly
|
|||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleTripIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperSupportGeoEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionProcessingService;
|
||||
|
|
@ -93,6 +94,7 @@ class TachographFileSessionControllerTest {
|
|||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
List.of(new TachographEsperActivityIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
|
|
@ -185,7 +187,27 @@ class TachographFileSessionControllerTest {
|
|||
"12:REG-1",
|
||||
"12:REG-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
"VIN-2",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"UNKNOWN",
|
||||
null,
|
||||
null
|
||||
)),
|
||||
List.of(),
|
||||
List.of(new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
|
|
@ -203,7 +225,27 @@ class TachographFileSessionControllerTest {
|
|||
"12:REG-1",
|
||||
"12:REG-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
"VIN-2",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"UNKNOWN",
|
||||
null,
|
||||
null
|
||||
)),
|
||||
List.of(),
|
||||
List.<TachographEsperPotentialInVehicleTripIntervalEvent>of(),
|
||||
|
|
@ -234,6 +276,25 @@ class TachographFileSessionControllerTest {
|
|||
"VIN-1",
|
||||
"VIN-2"
|
||||
)),
|
||||
List.of(new TachographEsperSupportGeoEvent(
|
||||
"SUP-1",
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-12T10:15:00Z"),
|
||||
"POSITION",
|
||||
"POSITION_RECORDED",
|
||||
"SNAPSHOT",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
java.math.BigDecimal.valueOf(48.2082),
|
||||
java.math.BigDecimal.valueOf(16.3738),
|
||||
123L,
|
||||
"path"
|
||||
)),
|
||||
List.of("note")
|
||||
));
|
||||
when(processingService.evaluateOperatingPeriods(eq(sessionId), eq("12:123"), org.mockito.ArgumentMatchers.any(TachographOperatingPeriodsProcessingRequest.class)))
|
||||
|
|
@ -307,6 +368,8 @@ class TachographFileSessionControllerTest {
|
|||
.andExpect(jsonPath("$.potentialInVehicleOvernightStayIntervalCount").value(0))
|
||||
.andExpect(jsonPath("$.potentialInVehicleTripIntervalCount").value(0))
|
||||
.andExpect(jsonPath("$.vuCardAbsentIntervalCount").value(1))
|
||||
.andExpect(jsonPath("$.supportGeoEventCount").value(1))
|
||||
.andExpect(jsonPath("$.supportGeoEvents[0].eventId").value("SUP-1"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].previousRegistrationKey").value("12:REG-1"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].nextRegistrationKey").value("12:REG-2"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].previousVehicleKey").value("VIN-1"))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package at.procon.eventhub.tachographfilesession.service;
|
||||
|
||||
import at.procon.eventhub.config.EventHubProperties;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||
|
|
@ -26,7 +28,7 @@ class DriverTimelineReusableProjectionBuilderTest {
|
|||
|
||||
private final DriverTimelineBuilder legacyBuilder = new DriverTimelineBuilder();
|
||||
private final DriverTimelineReusableProjectionBuilder reusableBuilder =
|
||||
new DriverTimelineReusableProjectionBuilder(legacyBuilder);
|
||||
new DriverTimelineReusableProjectionBuilder(legacyBuilder, new EventHubProperties());
|
||||
|
||||
@Test
|
||||
void matchesLegacyDrivingDerivedProjectionChain() {
|
||||
|
|
@ -130,7 +132,17 @@ class DriverTimelineReusableProjectionBuilderTest {
|
|||
assertThat(coverageInterval.cardPresentCoveragePercent()).isEqualTo(0.0d);
|
||||
assertThat(reusableBundle.drivingInterruptionVehicleChangeIntervals()).containsExactlyElementsOf(legacyDrivingInterruptionVehicleChanges);
|
||||
assertThat(reusableBundle.vuCardAbsentIntervals()).containsExactlyElementsOf(legacyVuCardAbsentIntervals);
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals()).containsExactlyElementsOf(legacyPotentialHomeOvernightStays);
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals()).hasSize(1);
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals().get(0).startedAt())
|
||||
.isEqualTo(legacyPotentialHomeOvernightStays.get(0).startedAt());
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals().get(0).endedAt())
|
||||
.isEqualTo(legacyPotentialHomeOvernightStays.get(0).endedAt());
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals().get(0).unknownCoveragePercent())
|
||||
.isEqualTo(legacyPotentialHomeOvernightStays.get(0).unknownCoveragePercent());
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals().get(0).beginBoundaryOdometerKm())
|
||||
.isEqualTo(200L);
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals().get(0).endBoundaryOdometerKm())
|
||||
.isEqualTo(201L);
|
||||
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals()).isEmpty();
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +192,56 @@ class DriverTimelineReusableProjectionBuilderTest {
|
|||
"b"
|
||||
)
|
||||
),
|
||||
List.of(),
|
||||
List.of(
|
||||
new ExtractedSupportEvent(
|
||||
"SUP-1",
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-01T09:45:00Z"),
|
||||
"POSITION",
|
||||
"POSITION_RECORDED",
|
||||
"SNAPSHOT",
|
||||
null,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new java.math.BigDecimal("48.2082"),
|
||||
new java.math.BigDecimal("16.3738"),
|
||||
null,
|
||||
150L,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"geo-1"
|
||||
),
|
||||
new ExtractedSupportEvent(
|
||||
"SUP-2",
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-02T00:15:00Z"),
|
||||
"POSITION",
|
||||
"POSITION_RECORDED",
|
||||
"SNAPSHOT",
|
||||
null,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new java.math.BigDecimal("48.2090"),
|
||||
new java.math.BigDecimal("16.3740"),
|
||||
null,
|
||||
150L,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"geo-2"
|
||||
)
|
||||
),
|
||||
List.of()
|
||||
);
|
||||
TachographFileSession session = new TachographFileSession(
|
||||
|
|
@ -209,6 +270,26 @@ class DriverTimelineReusableProjectionBuilderTest {
|
|||
.isEqualTo(100.0d);
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).unknownCoveragePercent())
|
||||
.isEqualTo(0.0d);
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).beginGeoEventId())
|
||||
.isEqualTo("SUP-1");
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).beginGeoEvent())
|
||||
.extracting("eventId", "eventDomain")
|
||||
.containsExactly("SUP-1", "POSITION");
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).endGeoEventId())
|
||||
.isEqualTo("SUP-2");
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).endGeoEvent())
|
||||
.extracting("eventId", "eventDomain")
|
||||
.containsExactly("SUP-2", "POSITION");
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).beginGeoDistanceSeconds())
|
||||
.isEqualTo(900L);
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).endGeoDistanceSeconds())
|
||||
.isEqualTo(900L);
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).beginBoundaryOdometerKm())
|
||||
.isEqualTo(100L);
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).endBoundaryOdometerKm())
|
||||
.isEqualTo(260L);
|
||||
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).geoEvidenceMovementCategory())
|
||||
.isEqualTo("STATIONARY");
|
||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals()).isEmpty();
|
||||
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals()).hasSize(1);
|
||||
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).startedAt())
|
||||
|
|
@ -217,5 +298,10 @@ class DriverTimelineReusableProjectionBuilderTest {
|
|||
.isEqualTo(OffsetDateTime.parse("2026-05-02T00:00:00Z"));
|
||||
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).cardPresentCoveragePercent())
|
||||
.isEqualTo(100.0d);
|
||||
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).beginGeoEventId())
|
||||
.isEqualTo("SUP-1");
|
||||
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).beginGeoEvent())
|
||||
.extracting("eventId", "eventDomain")
|
||||
.containsExactly("SUP-1", "POSITION");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -119,7 +119,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -226,7 +226,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -315,7 +315,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -417,7 +417,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -502,7 +502,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -594,7 +594,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
@ -668,7 +668,7 @@ class TachographFileSessionProcessingServiceTest {
|
|||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
driverTimelineBuilder,
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder, new EventHubProperties()),
|
||||
new EventBackedDriverTimelineBuilder(
|
||||
new IntervalBackedDriverTimelineEventBuilder(
|
||||
driverTimelineBuilder,
|
||||
|
|
|
|||
Loading…
Reference in New Issue