Add rest candidate Esper projections
This commit is contained in:
parent
eb4e04f144
commit
3b2f893246
|
|
@ -386,7 +386,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"significantDrivingMinutes\": 3\n}"
|
||||
"raw": "{\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/eventhub/tachograph-file-sessions/{{sessionId}}/drivers/{{driverKey}}/processing/esper-events",
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ public class EventHubProperties {
|
|||
public static class Processing {
|
||||
private int operatingSplitIdleHours = 7;
|
||||
private int significantDrivingMinutes = 3;
|
||||
private int minimumRestPeriodMinutes = 720;
|
||||
private int mergeGapSeconds = 0;
|
||||
private int gapDetectionToleranceSeconds = 0;
|
||||
|
||||
|
|
@ -377,6 +378,14 @@ public class EventHubProperties {
|
|||
this.significantDrivingMinutes = Math.max(1, significantDrivingMinutes);
|
||||
}
|
||||
|
||||
public int getMinimumRestPeriodMinutes() {
|
||||
return minimumRestPeriodMinutes;
|
||||
}
|
||||
|
||||
public void setMinimumRestPeriodMinutes(int minimumRestPeriodMinutes) {
|
||||
this.minimumRestPeriodMinutes = Math.max(1, minimumRestPeriodMinutes);
|
||||
}
|
||||
|
||||
public int getMergeGapSeconds() {
|
||||
return mergeGapSeconds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package at.procon.eventhub.tachographfilesession.dto;
|
|||
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import java.time.OffsetDateTime;
|
||||
|
|
@ -19,11 +20,15 @@ public record TachographEsperDriverProcessingResultDto(
|
|||
int activityIntervalCount,
|
||||
int drivingIntervalCount,
|
||||
int drivingInterruptionIntervalCount,
|
||||
int drivingInterruptionVehicleChangeIntervalCount,
|
||||
int potentialHomeOvernightStayIntervalCount,
|
||||
int vehicleUsageIntervalCount,
|
||||
int vuCardAbsentIntervalCount,
|
||||
List<TachographEsperActivityIntervalEvent> activityIntervals,
|
||||
List<TachographEsperActivityIntervalEvent> drivingIntervals,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals,
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals,
|
||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||
List<String> notes
|
||||
|
|
|
|||
|
|
@ -5,9 +5,13 @@ import java.time.OffsetDateTime;
|
|||
public record TachographEsperEventsProcessingRequest(
|
||||
OffsetDateTime occurredFrom,
|
||||
OffsetDateTime occurredTo,
|
||||
Integer significantDrivingMinutes
|
||||
Integer significantDrivingMinutes,
|
||||
Integer minimumRestPeriodMinutes
|
||||
) {
|
||||
public TachographEsperEventsProcessingRequest {
|
||||
significantDrivingMinutes = significantDrivingMinutes == null ? null : Math.max(1, significantDrivingMinutes);
|
||||
minimumRestPeriodMinutes = minimumRestPeriodMinutes == null
|
||||
? null
|
||||
: Math.max(1, minimumRestPeriodMinutes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ public record TachographEsperDrivingInterruptionIntervalEvent(
|
|||
long durationSeconds,
|
||||
String previousDrivingSourceIntervalId,
|
||||
String nextDrivingSourceIntervalId,
|
||||
String previousRegistrationKey,
|
||||
String nextRegistrationKey,
|
||||
String previousVehicleKey,
|
||||
String nextVehicleKey
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public record TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
OffsetDateTime startedAt,
|
||||
OffsetDateTime endedAt,
|
||||
long durationSeconds,
|
||||
long unknownDurationSeconds,
|
||||
double unknownCoveragePercent,
|
||||
String previousDrivingSourceIntervalId,
|
||||
String nextDrivingSourceIntervalId,
|
||||
String previousRegistrationKey,
|
||||
String nextRegistrationKey,
|
||||
String previousVehicleKey,
|
||||
String nextVehicleKey
|
||||
) {
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
|||
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||
|
|
@ -52,6 +53,10 @@ public class DriverTimelineBuilder {
|
|||
loadResource("esper/tachograph-driving-interval-events.epl");
|
||||
private static final String DRIVING_INTERRUPTION_INTERVAL_EVENTS_EPL_TEMPLATE =
|
||||
loadResource("esper/tachograph-driving-interruption-interval-events.epl");
|
||||
private static final String DRIVING_INTERRUPTION_VEHICLE_CHANGE_INTERVAL_EVENTS_EPL =
|
||||
loadResource("esper/tachograph-driving-interruption-vehicle-change-interval-events.epl");
|
||||
private static final String POTENTIAL_HOME_OVERNIGHT_STAY_INTERVAL_EVENTS_EPL_TEMPLATE =
|
||||
loadResource("esper/tachograph-potential-home-overnight-stay-interval-events.epl");
|
||||
private static final String VEHICLE_USAGE_INTERVAL_EVENTS_EPL =
|
||||
loadResource("esper/tachograph-vehicle-usage-interval-events.epl");
|
||||
private static final String VU_CARD_ABSENT_INTERVAL_EVENTS_EPL =
|
||||
|
|
@ -176,6 +181,77 @@ public class DriverTimelineBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
public List<TachographEsperDrivingInterruptionIntervalEvent> buildEsperDrivingInterruptionVehicleChangeIntervalEvents(
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals
|
||||
) {
|
||||
if (drivingInterruptionIntervals == null || drivingInterruptionIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographDrivingInterruptionIntervalInputEvent",
|
||||
drivingInterruptionIntervalInputDefinition()
|
||||
),
|
||||
DRIVING_INTERRUPTION_VEHICLE_CHANGE_INTERVAL_EVENTS_EPL,
|
||||
"drivingInterruptionVehicleChangeIntervals",
|
||||
newData -> collectDrivingInterruptionIntervalEventsFromTimestamps(newData, result),
|
||||
runtime -> {
|
||||
for (TachographEsperDrivingInterruptionIntervalEvent interval : drivingInterruptionIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toDrivingInterruptionIntervalInputMap(interval),
|
||||
"TachographDrivingInterruptionIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<TachographEsperPotentialHomeOvernightStayIntervalEvent> buildEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||
int minimumRestPeriodMinutes
|
||||
) {
|
||||
if (drivingInterruptionIntervals == null
|
||||
|| drivingInterruptionIntervals.isEmpty()
|
||||
|| vuCardAbsentIntervals == null
|
||||
|| vuCardAbsentIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> {
|
||||
configuration.getCommon().addEventType(
|
||||
"TachographDrivingInterruptionIntervalInputEvent",
|
||||
drivingInterruptionIntervalInputDefinition()
|
||||
);
|
||||
configuration.getCommon().addEventType(
|
||||
"TachographVuCardAbsentIntervalInputEvent",
|
||||
vuCardAbsentIntervalInputDefinition()
|
||||
);
|
||||
},
|
||||
renderPotentialHomeOvernightStayIntervalEventsEpl(minimumRestPeriodMinutes),
|
||||
"potentialHomeOvernightStayIntervals",
|
||||
newData -> collectPotentialHomeOvernightStayIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (TachographEsperVuCardAbsentIntervalEvent interval : vuCardAbsentIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toVuCardAbsentIntervalInputMap(interval),
|
||||
"TachographVuCardAbsentIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
for (TachographEsperDrivingInterruptionIntervalEvent interval : drivingInterruptionIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toDrivingInterruptionIntervalInputMap(interval),
|
||||
"TachographDrivingInterruptionIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<TachographEsperVuCardAbsentIntervalEvent> buildEsperVuCardAbsentIntervalEvents(
|
||||
TachographFileSession session,
|
||||
DriverExtractionSession driverSession
|
||||
|
|
@ -602,6 +678,42 @@ public class DriverTimelineBuilder {
|
|||
return definition;
|
||||
}
|
||||
|
||||
private Map<String, Object> drivingInterruptionIntervalInputDefinition() {
|
||||
Map<String, Object> definition = new LinkedHashMap<>();
|
||||
definition.put("sessionId", UUID.class);
|
||||
definition.put("driverKey", String.class);
|
||||
definition.put("startedAt", OffsetDateTime.class);
|
||||
definition.put("endedAt", OffsetDateTime.class);
|
||||
definition.put("startedAtEpochSecond", long.class);
|
||||
definition.put("endedAtEpochSecond", long.class);
|
||||
definition.put("durationSeconds", long.class);
|
||||
definition.put("previousDrivingSourceIntervalId", String.class);
|
||||
definition.put("nextDrivingSourceIntervalId", String.class);
|
||||
definition.put("previousRegistrationKey", String.class);
|
||||
definition.put("nextRegistrationKey", String.class);
|
||||
definition.put("previousVehicleKey", String.class);
|
||||
definition.put("nextVehicleKey", String.class);
|
||||
return definition;
|
||||
}
|
||||
|
||||
private Map<String, Object> vuCardAbsentIntervalInputDefinition() {
|
||||
Map<String, Object> definition = new LinkedHashMap<>();
|
||||
definition.put("sessionId", UUID.class);
|
||||
definition.put("driverKey", String.class);
|
||||
definition.put("startedAt", OffsetDateTime.class);
|
||||
definition.put("endedAt", OffsetDateTime.class);
|
||||
definition.put("startedAtEpochSecond", long.class);
|
||||
definition.put("endedAtEpochSecond", long.class);
|
||||
definition.put("durationSeconds", long.class);
|
||||
definition.put("previousUsageIntervalId", String.class);
|
||||
definition.put("nextUsageIntervalId", String.class);
|
||||
definition.put("previousRegistrationKey", String.class);
|
||||
definition.put("nextRegistrationKey", String.class);
|
||||
definition.put("previousVehicleKey", String.class);
|
||||
definition.put("nextVehicleKey", String.class);
|
||||
return definition;
|
||||
}
|
||||
|
||||
private Map<String, Object> toActivityIntervalInputMap(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
|
|
@ -663,6 +775,44 @@ public class DriverTimelineBuilder {
|
|||
return event;
|
||||
}
|
||||
|
||||
private Map<String, Object> toDrivingInterruptionIntervalInputMap(
|
||||
TachographEsperDrivingInterruptionIntervalEvent interval
|
||||
) {
|
||||
Map<String, Object> event = new LinkedHashMap<>();
|
||||
event.put("sessionId", interval.sessionId());
|
||||
event.put("driverKey", interval.driverKey());
|
||||
event.put("startedAt", interval.startedAt());
|
||||
event.put("endedAt", interval.endedAt());
|
||||
event.put("startedAtEpochSecond", interval.startedAt().toEpochSecond());
|
||||
event.put("endedAtEpochSecond", interval.endedAt().toEpochSecond());
|
||||
event.put("durationSeconds", interval.durationSeconds());
|
||||
event.put("previousDrivingSourceIntervalId", interval.previousDrivingSourceIntervalId());
|
||||
event.put("nextDrivingSourceIntervalId", interval.nextDrivingSourceIntervalId());
|
||||
event.put("previousRegistrationKey", interval.previousRegistrationKey());
|
||||
event.put("nextRegistrationKey", interval.nextRegistrationKey());
|
||||
event.put("previousVehicleKey", interval.previousVehicleKey());
|
||||
event.put("nextVehicleKey", interval.nextVehicleKey());
|
||||
return event;
|
||||
}
|
||||
|
||||
private Map<String, Object> toVuCardAbsentIntervalInputMap(TachographEsperVuCardAbsentIntervalEvent interval) {
|
||||
Map<String, Object> event = new LinkedHashMap<>();
|
||||
event.put("sessionId", interval.sessionId());
|
||||
event.put("driverKey", interval.driverKey());
|
||||
event.put("startedAt", interval.startedAt());
|
||||
event.put("endedAt", interval.endedAt());
|
||||
event.put("startedAtEpochSecond", interval.startedAt().toEpochSecond());
|
||||
event.put("endedAtEpochSecond", interval.endedAt().toEpochSecond());
|
||||
event.put("durationSeconds", interval.durationSeconds());
|
||||
event.put("previousUsageIntervalId", interval.previousUsageIntervalId());
|
||||
event.put("nextUsageIntervalId", interval.nextUsageIntervalId());
|
||||
event.put("previousRegistrationKey", interval.previousRegistrationKey());
|
||||
event.put("nextRegistrationKey", interval.nextRegistrationKey());
|
||||
event.put("previousVehicleKey", interval.previousVehicleKey());
|
||||
event.put("nextVehicleKey", interval.nextVehicleKey());
|
||||
return event;
|
||||
}
|
||||
|
||||
private String firstSourceIntervalId(ResolvedVehicleUsageInterval interval) {
|
||||
return interval.sourceIntervalIds().isEmpty() ? interval.intervalId() : interval.sourceIntervalIds().get(0);
|
||||
}
|
||||
|
|
@ -746,6 +896,32 @@ public class DriverTimelineBuilder {
|
|||
(Long) event.get("durationSeconds"),
|
||||
(String) event.get("previousDrivingSourceIntervalId"),
|
||||
(String) event.get("nextDrivingSourceIntervalId"),
|
||||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDrivingInterruptionIntervalEventsFromTimestamps(
|
||||
EventBean[] newData,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> target
|
||||
) {
|
||||
if (newData == null) {
|
||||
return;
|
||||
}
|
||||
for (EventBean event : newData) {
|
||||
target.add(new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
(OffsetDateTime) event.get("startedAt"),
|
||||
(OffsetDateTime) event.get("endedAt"),
|
||||
(Long) event.get("durationSeconds"),
|
||||
(String) event.get("previousDrivingSourceIntervalId"),
|
||||
(String) event.get("nextDrivingSourceIntervalId"),
|
||||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
));
|
||||
|
|
@ -778,6 +954,32 @@ public class DriverTimelineBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void collectPotentialHomeOvernightStayIntervalEvents(
|
||||
EventBean[] newData,
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> target
|
||||
) {
|
||||
if (newData == null) {
|
||||
return;
|
||||
}
|
||||
for (EventBean event : newData) {
|
||||
target.add(new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
(OffsetDateTime) event.get("startedAt"),
|
||||
(OffsetDateTime) event.get("endedAt"),
|
||||
(Long) event.get("durationSeconds"),
|
||||
(Long) event.get("unknownDurationSeconds"),
|
||||
(Double) event.get("unknownCoveragePercent"),
|
||||
(String) event.get("previousDrivingSourceIntervalId"),
|
||||
(String) event.get("nextDrivingSourceIntervalId"),
|
||||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String> castSourceIntervalIds(Object value) {
|
||||
return value == null ? List.of() : List.copyOf((List<String>) value);
|
||||
|
|
@ -799,4 +1001,12 @@ public class DriverTimelineBuilder {
|
|||
Long.toString(thresholdSeconds)
|
||||
);
|
||||
}
|
||||
|
||||
private String renderPotentialHomeOvernightStayIntervalEventsEpl(int minimumRestPeriodMinutes) {
|
||||
long thresholdSeconds = Math.max(1, minimumRestPeriodMinutes) * 60L;
|
||||
return POTENTIAL_HOME_OVERNIGHT_STAY_INTERVAL_EVENTS_EPL_TEMPLATE.replace(
|
||||
"${POTENTIAL_HOME_OVERNIGHT_STAY_THRESHOLD_SECONDS}",
|
||||
Long.toString(thresholdSeconds)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
|||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import java.time.Duration;
|
||||
|
|
@ -132,7 +133,7 @@ public class TachographFileSessionProcessingService {
|
|||
TachographEsperEventsProcessingRequest request
|
||||
) {
|
||||
TachographEsperEventsProcessingRequest effectiveRequest = request == null
|
||||
? new TachographEsperEventsProcessingRequest(null, null, null)
|
||||
? new TachographEsperEventsProcessingRequest(null, null, null, null)
|
||||
: request;
|
||||
TachographFileSession session = repository.find(sessionId)
|
||||
.orElseThrow(() -> new TachographFileSessionNotFoundException(sessionId));
|
||||
|
|
@ -148,6 +149,7 @@ public class TachographFileSessionProcessingService {
|
|||
throw new IllegalArgumentException("occurredTo must not be before occurredFrom.");
|
||||
}
|
||||
int significantDrivingMinutes = resolveEsperSignificantDrivingMinutes(effectiveRequest);
|
||||
int minimumRestPeriodMinutes = resolveMinimumRestPeriodMinutes(effectiveRequest);
|
||||
|
||||
List<TachographEsperActivityIntervalEvent> activityIntervals = clipEsperActivityIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperActivityIntervalEvents(sessionId, driverKey, timeline),
|
||||
|
|
@ -159,24 +161,47 @@ public class TachographFileSessionProcessingService {
|
|||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionIntervals =
|
||||
driverTimelineBuilder.buildEsperDrivingInterruptionIntervalEvents(
|
||||
sessionId,
|
||||
driverKey,
|
||||
timeline,
|
||||
significantDrivingMinutes
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals =
|
||||
clipEsperDrivingInterruptionIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperDrivingInterruptionIntervalEvents(
|
||||
sessionId,
|
||||
driverKey,
|
||||
timeline,
|
||||
significantDrivingMinutes
|
||||
rawDrivingInterruptionIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals =
|
||||
clipEsperDrivingInterruptionIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperDrivingInterruptionVehicleChangeIntervalEvents(
|
||||
rawDrivingInterruptionIntervals
|
||||
),
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals =
|
||||
driverTimelineBuilder.buildEsperVuCardAbsentIntervalEvents(timeline);
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals =
|
||||
clipEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
rawDrivingInterruptionIntervals,
|
||||
rawVuCardAbsentIntervals,
|
||||
minimumRestPeriodMinutes
|
||||
),
|
||||
rawVuCardAbsentIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals = clipEsperVehicleUsageIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperVehicleUsageIntervalEvents(timeline),
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals = clipEsperVuCardAbsentIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperVuCardAbsentIntervalEvents(timeline),
|
||||
rawVuCardAbsentIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
|
|
@ -192,11 +217,15 @@ public class TachographFileSessionProcessingService {
|
|||
activityIntervals.size(),
|
||||
drivingIntervals.size(),
|
||||
drivingInterruptionIntervals.size(),
|
||||
drivingInterruptionVehicleChangeIntervals.size(),
|
||||
potentialHomeOvernightStayIntervals.size(),
|
||||
vehicleUsageIntervals.size(),
|
||||
vuCardAbsentIntervals.size(),
|
||||
activityIntervals,
|
||||
drivingIntervals,
|
||||
drivingInterruptionIntervals,
|
||||
drivingInterruptionVehicleChangeIntervals,
|
||||
potentialHomeOvernightStayIntervals,
|
||||
vehicleUsageIntervals,
|
||||
vuCardAbsentIntervals,
|
||||
esperProjectionNotes()
|
||||
|
|
@ -310,6 +339,8 @@ public class TachographFileSessionProcessingService {
|
|||
Duration.between(start, end).getSeconds(),
|
||||
interval.previousDrivingSourceIntervalId(),
|
||||
interval.nextDrivingSourceIntervalId(),
|
||||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
);
|
||||
|
|
@ -355,6 +386,49 @@ public class TachographFileSessionProcessingService {
|
|||
.toList();
|
||||
}
|
||||
|
||||
private List<TachographEsperPotentialHomeOvernightStayIntervalEvent> clipEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals,
|
||||
OffsetDateTime requestedFrom,
|
||||
OffsetDateTime requestedTo
|
||||
) {
|
||||
if (requestedFrom == null || requestedTo == null) {
|
||||
return List.of();
|
||||
}
|
||||
return intervals.stream()
|
||||
.map(interval -> {
|
||||
OffsetDateTime start = max(interval.startedAt(), requestedFrom);
|
||||
OffsetDateTime end = min(interval.endedAt(), requestedTo);
|
||||
if (!end.isAfter(start)) {
|
||||
return null;
|
||||
}
|
||||
long durationSeconds = Duration.between(start, end).getSeconds();
|
||||
long unknownDurationSeconds = overlapSeconds(start, end, rawVuCardAbsentIntervals, interval.driverKey());
|
||||
double unknownCoveragePercent = durationSeconds == 0L
|
||||
? 0.0d
|
||||
: (unknownDurationSeconds * 100.0d) / durationSeconds;
|
||||
return new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
start,
|
||||
end,
|
||||
durationSeconds,
|
||||
unknownDurationSeconds,
|
||||
unknownCoveragePercent,
|
||||
interval.previousDrivingSourceIntervalId(),
|
||||
interval.nextDrivingSourceIntervalId(),
|
||||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(TachographEsperPotentialHomeOvernightStayIntervalEvent::startedAt)
|
||||
.thenComparing(TachographEsperPotentialHomeOvernightStayIntervalEvent::endedAt))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<ResolvedActivityInterval> synthesizeUnknownGaps(
|
||||
List<ResolvedActivityInterval> knownIntervals,
|
||||
Duration gapDetectionTolerance
|
||||
|
|
@ -899,6 +973,12 @@ public class TachographFileSessionProcessingService {
|
|||
: request.significantDrivingMinutes();
|
||||
}
|
||||
|
||||
private int resolveMinimumRestPeriodMinutes(TachographEsperEventsProcessingRequest request) {
|
||||
return request.minimumRestPeriodMinutes() == null
|
||||
? properties.getTachographFileSession().getProcessing().getMinimumRestPeriodMinutes()
|
||||
: request.minimumRestPeriodMinutes();
|
||||
}
|
||||
|
||||
private List<String> notes() {
|
||||
return List.of(
|
||||
"This endpoint evaluates operating periods from the in-memory tachograph file-session model.",
|
||||
|
|
@ -913,12 +993,37 @@ public class TachographFileSessionProcessingService {
|
|||
"This endpoint returns Esper-backed per-driver interval projections from the in-memory tachograph file-session model.",
|
||||
"Driving intervals are a filtered projection of activity intervals where activityType = DRIVE.",
|
||||
"Driving interruption intervals are gaps between consecutive driving intervals longer than the configured significant-driving threshold.",
|
||||
"Driving interruption vehicle-change intervals are DTI intervals where previousRegistrationKey differs from nextRegistrationKey.",
|
||||
"Potential home overnight stay intervals are DTI intervals longer than the configured minimum rest-period threshold where VU card-absent overlap covers at least 95% of the DTI.",
|
||||
"VU card-absent intervals are gaps between consecutive normalized vehicle-usage intervals for the same driver.",
|
||||
"occurredFrom and occurredTo clip the returned interval projections to the requested UTC time window.",
|
||||
"Vehicle-usage intervals clear clipped odometer endpoints because boundary odometer values cannot be recomputed safely from the source interval."
|
||||
);
|
||||
}
|
||||
|
||||
private long overlapSeconds(
|
||||
OffsetDateTime intervalStart,
|
||||
OffsetDateTime intervalEnd,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> unknownIntervals,
|
||||
String driverKey
|
||||
) {
|
||||
if (unknownIntervals == null || unknownIntervals.isEmpty()) {
|
||||
return 0L;
|
||||
}
|
||||
long total = 0L;
|
||||
for (TachographEsperVuCardAbsentIntervalEvent unknown : unknownIntervals) {
|
||||
if (!Objects.equals(driverKey, unknown.driverKey())) {
|
||||
continue;
|
||||
}
|
||||
OffsetDateTime overlapStart = max(intervalStart, unknown.startedAt());
|
||||
OffsetDateTime overlapEnd = min(intervalEnd, unknown.endedAt());
|
||||
if (overlapEnd.isAfter(overlapStart)) {
|
||||
total += Duration.between(overlapStart, overlapEnd).getSeconds();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private OffsetDateTime utc(OffsetDateTime value) {
|
||||
return value == null ? null : value.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ create schema SignificantDrivingInterval(
|
|||
startedAtEpochSecond long,
|
||||
endedAtEpochSecond long,
|
||||
durationSeconds long,
|
||||
registrationKey string,
|
||||
vehicleKey string
|
||||
);
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ create schema DrivingInterruptionInterval(
|
|||
durationSeconds long,
|
||||
previousDrivingSourceIntervalId string,
|
||||
nextDrivingSourceIntervalId string,
|
||||
previousRegistrationKey string,
|
||||
nextRegistrationKey string,
|
||||
previousVehicleKey string,
|
||||
nextVehicleKey string
|
||||
);
|
||||
|
|
@ -30,6 +33,7 @@ select
|
|||
startedAtEpochSecond,
|
||||
endedAtEpochSecond,
|
||||
durationSeconds,
|
||||
registrationKey,
|
||||
vehicleKey
|
||||
from TachographActivityIntervalInputEvent(activityType = 'DRIVE', durationSeconds > ${SIGNIFICANT_DRIVING_THRESHOLD_SECONDS});
|
||||
|
||||
|
|
@ -45,6 +49,8 @@ select
|
|||
next.startedAtEpochSecond - priorInterval.endedAtEpochSecond as durationSeconds,
|
||||
priorInterval.lastSourceIntervalId as previousDrivingSourceIntervalId,
|
||||
next.firstSourceIntervalId as nextDrivingSourceIntervalId,
|
||||
priorInterval.registrationKey as previousRegistrationKey,
|
||||
next.registrationKey as nextRegistrationKey,
|
||||
priorInterval.vehicleKey as previousVehicleKey,
|
||||
next.vehicleKey as nextVehicleKey
|
||||
from PreviousSignificantDrivingInterval as priorInterval
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
@name('drivingInterruptionVehicleChangeIntervals')
|
||||
select
|
||||
sessionId,
|
||||
driverKey,
|
||||
startedAt,
|
||||
endedAt,
|
||||
durationSeconds,
|
||||
previousDrivingSourceIntervalId,
|
||||
nextDrivingSourceIntervalId,
|
||||
previousRegistrationKey,
|
||||
nextRegistrationKey,
|
||||
previousVehicleKey,
|
||||
nextVehicleKey
|
||||
from TachographDrivingInterruptionIntervalInputEvent(
|
||||
previousRegistrationKey is not null,
|
||||
nextRegistrationKey is not null,
|
||||
previousRegistrationKey != nextRegistrationKey
|
||||
);
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
@name('potentialHomeOvernightStayIntervals')
|
||||
select
|
||||
d.sessionId as sessionId,
|
||||
d.driverKey as driverKey,
|
||||
d.startedAt as startedAt,
|
||||
d.endedAt as endedAt,
|
||||
d.durationSeconds as durationSeconds,
|
||||
sum(
|
||||
case
|
||||
when u.startedAtEpochSecond <= d.startedAtEpochSecond and u.endedAtEpochSecond >= d.endedAtEpochSecond
|
||||
then d.durationSeconds
|
||||
when u.startedAtEpochSecond <= d.startedAtEpochSecond
|
||||
then u.endedAtEpochSecond - d.startedAtEpochSecond
|
||||
when u.endedAtEpochSecond >= d.endedAtEpochSecond
|
||||
then d.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
end
|
||||
) as unknownDurationSeconds,
|
||||
(sum(
|
||||
case
|
||||
when u.startedAtEpochSecond <= d.startedAtEpochSecond and u.endedAtEpochSecond >= d.endedAtEpochSecond
|
||||
then d.durationSeconds
|
||||
when u.startedAtEpochSecond <= d.startedAtEpochSecond
|
||||
then u.endedAtEpochSecond - d.startedAtEpochSecond
|
||||
when u.endedAtEpochSecond >= d.endedAtEpochSecond
|
||||
then d.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
end
|
||||
) * 100.0d) / d.durationSeconds as unknownCoveragePercent,
|
||||
d.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||
d.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||
d.previousRegistrationKey as previousRegistrationKey,
|
||||
d.nextRegistrationKey as nextRegistrationKey,
|
||||
d.previousVehicleKey as previousVehicleKey,
|
||||
d.nextVehicleKey as nextVehicleKey
|
||||
from TachographDrivingInterruptionIntervalInputEvent(durationSeconds > ${POTENTIAL_HOME_OVERNIGHT_STAY_THRESHOLD_SECONDS}) as d unidirectional,
|
||||
TachographVuCardAbsentIntervalInputEvent#keepall as u
|
||||
where u.driverKey = d.driverKey
|
||||
and u.startedAtEpochSecond < d.endedAtEpochSecond
|
||||
and u.endedAtEpochSecond > d.startedAtEpochSecond
|
||||
group by
|
||||
d.sessionId,
|
||||
d.driverKey,
|
||||
d.startedAt,
|
||||
d.endedAt,
|
||||
d.startedAtEpochSecond,
|
||||
d.endedAtEpochSecond,
|
||||
d.durationSeconds,
|
||||
d.previousDrivingSourceIntervalId,
|
||||
d.nextDrivingSourceIntervalId,
|
||||
d.previousRegistrationKey,
|
||||
d.nextRegistrationKey,
|
||||
d.previousVehicleKey,
|
||||
d.nextVehicleKey
|
||||
having sum(
|
||||
case
|
||||
when u.startedAtEpochSecond <= d.startedAtEpochSecond and u.endedAtEpochSecond >= d.endedAtEpochSecond
|
||||
then d.durationSeconds
|
||||
when u.startedAtEpochSecond <= d.startedAtEpochSecond
|
||||
then u.endedAtEpochSecond - d.startedAtEpochSecond
|
||||
when u.endedAtEpochSecond >= d.endedAtEpochSecond
|
||||
then d.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
end
|
||||
) * 100L >= d.durationSeconds * 95L;
|
||||
|
|
@ -21,6 +21,7 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummary
|
|||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionProcessingService;
|
||||
|
|
@ -81,6 +82,8 @@ class TachographFileSessionControllerTest {
|
|||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
List.of(new TachographEsperActivityIntervalEvent(
|
||||
|
|
@ -129,6 +132,36 @@ class TachographFileSessionControllerTest {
|
|||
1800L,
|
||||
"ACT-2",
|
||||
"ACT-3",
|
||||
"12:REG-1",
|
||||
"12:REG-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
)),
|
||||
List.of(new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-12T10:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T10:30:00Z"),
|
||||
1800L,
|
||||
"ACT-2",
|
||||
"ACT-3",
|
||||
"12:REG-1",
|
||||
"12:REG-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
)),
|
||||
List.of(new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-12T10:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T22:00:00Z"),
|
||||
43_200L,
|
||||
43_200L,
|
||||
100.0d,
|
||||
"ACT-2",
|
||||
"ACT-3",
|
||||
"12:REG-1",
|
||||
"12:REG-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
)),
|
||||
|
|
@ -213,7 +246,8 @@ class TachographFileSessionControllerTest {
|
|||
{
|
||||
"occurredFrom": "2026-05-12T08:30:00Z",
|
||||
"occurredTo": "2026-05-12T11:30:00Z",
|
||||
"significantDrivingMinutes": 3
|
||||
"significantDrivingMinutes": 3,
|
||||
"minimumRestPeriodMinutes": 720
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
|
|
@ -223,9 +257,17 @@ class TachographFileSessionControllerTest {
|
|||
.andExpect(jsonPath("$.requestedTo").value("2026-05-12T11:30:00Z"))
|
||||
.andExpect(jsonPath("$.activityIntervalCount").value(2))
|
||||
.andExpect(jsonPath("$.drivingInterruptionIntervalCount").value(1))
|
||||
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervalCount").value(1))
|
||||
.andExpect(jsonPath("$.potentialHomeOvernightStayIntervalCount").value(1))
|
||||
.andExpect(jsonPath("$.vuCardAbsentIntervalCount").value(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"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].nextVehicleKey").value("VIN-2"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervals[0].previousRegistrationKey").value("12:REG-1"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervals[0].nextRegistrationKey").value("12:REG-2"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervals[0].previousVehicleKey").value("VIN-1"))
|
||||
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervals[0].nextVehicleKey").value("VIN-2"))
|
||||
.andExpect(jsonPath("$.drivingIntervals[0].activityType").value("DRIVE"));
|
||||
|
||||
mockMvc.perform(post("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers/{driverKey}/processing/operating-periods", sessionId, "12:123")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
|||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||
|
|
@ -448,7 +449,72 @@ class DriverTimelineBuilderTest {
|
|||
assertThat(interruptions.get(0).durationSeconds()).isEqualTo(480L);
|
||||
assertThat(interruptions.get(0).previousDrivingSourceIntervalId()).isEqualTo("ACT-1");
|
||||
assertThat(interruptions.get(0).nextDrivingSourceIntervalId()).isEqualTo("ACT-3");
|
||||
assertThat(interruptions.get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||
assertThat(interruptions.get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||
assertThat(interruptions.get(0).previousVehicleKey()).isEqualTo("VIN-1");
|
||||
assertThat(interruptions.get(0).nextVehicleKey()).isEqualTo("VIN-2");
|
||||
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> vehicleChangeInterruptions =
|
||||
builder.buildEsperDrivingInterruptionVehicleChangeIntervalEvents(interruptions);
|
||||
|
||||
assertThat(vehicleChangeInterruptions).hasSize(1);
|
||||
assertThat(vehicleChangeInterruptions.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:02:00Z"));
|
||||
assertThat(vehicleChangeInterruptions.get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||
assertThat(vehicleChangeInterruptions.get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||
assertThat(vehicleChangeInterruptions.get(0).previousVehicleKey()).isEqualTo("VIN-1");
|
||||
assertThat(vehicleChangeInterruptions.get(0).nextVehicleKey()).isEqualTo("VIN-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildsPotentialHomeOvernightStayIntervalsFromDtiAndVuCardAbsentOverlap() {
|
||||
UUID sessionId = UUID.randomUUID();
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> interruptions = List.of(
|
||||
new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||
50_400L,
|
||||
"ACT-1",
|
||||
"ACT-2",
|
||||
"12:REG-1",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"VIN-1"
|
||||
)
|
||||
);
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals = List.of(
|
||||
new TachographEsperVuCardAbsentIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-01T10:00:01Z"),
|
||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||
50_399L,
|
||||
"CVU-1",
|
||||
"CVU-2",
|
||||
"12:REG-1",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"VIN-1"
|
||||
)
|
||||
);
|
||||
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals =
|
||||
builder.buildEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
interruptions,
|
||||
vuCardAbsentIntervals,
|
||||
720
|
||||
);
|
||||
|
||||
assertThat(intervals).hasSize(1);
|
||||
assertThat(intervals.get(0).sessionId()).isEqualTo(sessionId);
|
||||
assertThat(intervals.get(0).driverKey()).isEqualTo("12:123");
|
||||
assertThat(intervals.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||
assertThat(intervals.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-02T00:00:00Z"));
|
||||
assertThat(intervals.get(0).durationSeconds()).isEqualTo(50_400L);
|
||||
assertThat(intervals.get(0).unknownDurationSeconds()).isEqualTo(50_399L);
|
||||
assertThat(intervals.get(0).unknownCoveragePercent()).isGreaterThan(99.9d);
|
||||
assertThat(intervals.get(0).previousDrivingSourceIntervalId()).isEqualTo("ACT-1");
|
||||
assertThat(intervals.get(0).nextDrivingSourceIntervalId()).isEqualTo("ACT-2");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ class TachographFileSessionProcessingServiceTest {
|
|||
assertThat(result.activityIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.drivingIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.vehicleUsageIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.vuCardAbsentIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.vuCardAbsentIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:01Z"));
|
||||
|
|
@ -155,7 +157,8 @@ class TachographFileSessionProcessingServiceTest {
|
|||
new TachographEsperEventsProcessingRequest(
|
||||
OffsetDateTime.parse("2026-05-01T08:45:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T12:30:00Z"),
|
||||
3
|
||||
3,
|
||||
720
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -166,10 +169,19 @@ class TachographFileSessionProcessingServiceTest {
|
|||
assertThat(result.activityIntervals().get(0).clippedToRequestedPeriod()).isTrue();
|
||||
assertThat(result.drivingIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.drivingInterruptionIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
|
||||
assertThat(result.drivingInterruptionIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||
assertThat(result.drivingInterruptionIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||
assertThat(result.drivingInterruptionIntervals().get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||
assertThat(result.drivingInterruptionIntervals().get(0).previousVehicleKey()).isEqualTo("VIN-1");
|
||||
assertThat(result.drivingInterruptionIntervals().get(0).nextVehicleKey()).isEqualTo("VIN-2");
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).previousVehicleKey()).isEqualTo("VIN-1");
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).nextVehicleKey()).isEqualTo("VIN-2");
|
||||
assertThat(result.vehicleUsageIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.vehicleUsageIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:45:00Z"));
|
||||
assertThat(result.vehicleUsageIntervals().get(0).odometerBeginKm()).isNull();
|
||||
|
|
@ -178,6 +190,80 @@ class TachographFileSessionProcessingServiceTest {
|
|||
assertThat(result.vuCardAbsentIntervalCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsPotentialHomeOvernightStayIntervalsWhenVuCardAbsentCoversLongDti() {
|
||||
EventHubProperties properties = new EventHubProperties();
|
||||
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||
repository,
|
||||
new DriverTimelineBuilder(),
|
||||
properties
|
||||
);
|
||||
|
||||
DriverExtractionSession driver = new DriverExtractionSession(
|
||||
"12:123",
|
||||
null,
|
||||
null,
|
||||
List.of(),
|
||||
List.of(),
|
||||
List.of(
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-1",
|
||||
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||
100L,
|
||||
200L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"vu-1"
|
||||
),
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-2",
|
||||
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-02T02:00:00Z"),
|
||||
201L,
|
||||
260L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"vu-2"
|
||||
)
|
||||
),
|
||||
List.of(
|
||||
new ExtractedCardActivityInterval("ACT-1", OffsetDateTime.parse("2026-05-01T08:00:00Z"), OffsetDateTime.parse("2026-05-01T10:00:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "a"),
|
||||
new ExtractedCardActivityInterval("ACT-2", OffsetDateTime.parse("2026-05-02T00:00:00Z"), OffsetDateTime.parse("2026-05-02T00:30:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "b")
|
||||
),
|
||||
List.of(),
|
||||
List.of()
|
||||
);
|
||||
TachographFileSession session = new TachographFileSession(
|
||||
UUID.randomUUID(),
|
||||
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 2, "42", "b", true, null),
|
||||
Map.of(driver.driverKey(), driver),
|
||||
new ExtractionStats(1, 2, 2, 1, 1, 0),
|
||||
List.of(),
|
||||
Instant.now(),
|
||||
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||
);
|
||||
repository.save(session);
|
||||
|
||||
TachographEsperDriverProcessingResultDto result = service.getEsperDriverProcessingResults(
|
||||
session.sessionId(),
|
||||
driver.driverKey(),
|
||||
new TachographEsperEventsProcessingRequest(
|
||||
OffsetDateTime.parse("2026-05-01T11:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T23:00:00Z"),
|
||||
3,
|
||||
720
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T23:00:00Z"));
|
||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).unknownDurationSeconds()).isEqualTo(43_200L);
|
||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).unknownCoveragePercent()).isEqualTo(100.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
void evaluatesOperatingPeriodsFromSessionTimeline() {
|
||||
EventHubProperties properties = new EventHubProperties();
|
||||
|
|
|
|||
Loading…
Reference in New Issue