Compare commits
4 Commits
9e6f8efb26
...
317983eba8
| Author | SHA1 | Date |
|---|---|---|
|
|
317983eba8 | |
|
|
3b2f893246 | |
|
|
eb4e04f144 | |
|
|
0e2b83270c |
|
|
@ -351,6 +351,61 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get tachograph file session driver Esper events",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/eventhub/tachograph-file-sessions/{{sessionId}}/drivers/{{driverKey}}/processing/esper-events",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"eventhub",
|
||||
"tachograph-file-sessions",
|
||||
"{{sessionId}}",
|
||||
"drivers",
|
||||
"{{driverKey}}",
|
||||
"processing",
|
||||
"esper-events"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Process tachograph file session Esper events",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"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",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"eventhub",
|
||||
"tachograph-file-sessions",
|
||||
"{{sessionId}}",
|
||||
"drivers",
|
||||
"{{driverKey}}",
|
||||
"processing",
|
||||
"esper-events"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Process tachograph file session operating periods",
|
||||
"request": {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package at.procon.eventhub.tachographfilesession.api;
|
||||
|
||||
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperEventsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||
|
|
@ -66,6 +68,23 @@ public class TachographFileSessionController {
|
|||
return ResponseEntity.ok(service.getDriver(sessionId, driverKey));
|
||||
}
|
||||
|
||||
@GetMapping("/{sessionId}/drivers/{driverKey}/processing/esper-events")
|
||||
public ResponseEntity<TachographEsperDriverProcessingResultDto> getEsperDriverProcessingResults(
|
||||
@PathVariable UUID sessionId,
|
||||
@PathVariable String driverKey
|
||||
) {
|
||||
return ResponseEntity.ok(processingService.getEsperDriverProcessingResults(sessionId, driverKey));
|
||||
}
|
||||
|
||||
@PostMapping("/{sessionId}/drivers/{driverKey}/processing/esper-events")
|
||||
public ResponseEntity<TachographEsperDriverProcessingResultDto> evaluateEsperDriverProcessingResults(
|
||||
@PathVariable UUID sessionId,
|
||||
@PathVariable String driverKey,
|
||||
@RequestBody(required = false) TachographEsperEventsProcessingRequest request
|
||||
) {
|
||||
return ResponseEntity.ok(processingService.getEsperDriverProcessingResults(sessionId, driverKey, request));
|
||||
}
|
||||
|
||||
@PostMapping("/{sessionId}/drivers/{driverKey}/processing/operating-periods")
|
||||
public ResponseEntity<TachographOperatingPeriodsProcessingResultDto> evaluateOperatingPeriods(
|
||||
@PathVariable UUID sessionId,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
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;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public record TachographEsperDriverProcessingResultDto(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
String sourceKind,
|
||||
OffsetDateTime loadedFrom,
|
||||
OffsetDateTime loadedTo,
|
||||
OffsetDateTime requestedFrom,
|
||||
OffsetDateTime requestedTo,
|
||||
int activityIntervalCount,
|
||||
int drivingIntervalCount,
|
||||
int drivingInterruptionIntervalCount,
|
||||
int drivingInterruptionVehicleChangeIntervalCount,
|
||||
int dailyWeeklyRestCandidateIntervalCount,
|
||||
int potentialHomeOvernightStayIntervalCount,
|
||||
int vehicleUsageIntervalCount,
|
||||
int vuCardAbsentIntervalCount,
|
||||
List<TachographEsperActivityIntervalEvent> activityIntervals,
|
||||
List<TachographEsperActivityIntervalEvent> drivingIntervals,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals,
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals,
|
||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||
List<String> notes
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package at.procon.eventhub.tachographfilesession.dto;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public record TachographEsperEventsProcessingRequest(
|
||||
OffsetDateTime occurredFrom,
|
||||
OffsetDateTime occurredTo,
|
||||
Integer significantDrivingMinutes,
|
||||
Integer minimumRestPeriodMinutes
|
||||
) {
|
||||
public TachographEsperEventsProcessingRequest {
|
||||
significantDrivingMinutes = significantDrivingMinutes == null ? null : Math.max(1, significantDrivingMinutes);
|
||||
minimumRestPeriodMinutes = minimumRestPeriodMinutes == null
|
||||
? null
|
||||
: Math.max(1, minimumRestPeriodMinutes);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,11 @@ package at.procon.eventhub.tachographfilesession.model;
|
|||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public record ResolvedVehicleUsageInterval(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
String intervalId,
|
||||
OffsetDateTime from,
|
||||
OffsetDateTime to,
|
||||
|
|
@ -17,6 +20,8 @@ public record ResolvedVehicleUsageInterval(
|
|||
List<String> sourceIntervalIds
|
||||
) {
|
||||
public static ResolvedVehicleUsageInterval resolved(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
String intervalId,
|
||||
OffsetDateTime from,
|
||||
OffsetDateTime to,
|
||||
|
|
@ -28,6 +33,8 @@ public record ResolvedVehicleUsageInterval(
|
|||
List<String> sourceIntervalIds
|
||||
) {
|
||||
return new ResolvedVehicleUsageInterval(
|
||||
sessionId,
|
||||
driverKey,
|
||||
intervalId,
|
||||
from,
|
||||
to,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public record TachographEsperActivityIntervalEvent(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
String intervalId,
|
||||
String activityType,
|
||||
String cardSlot,
|
||||
String cardStatus,
|
||||
String drivingStatus,
|
||||
String registrationKey,
|
||||
String vehicleKey,
|
||||
String sourceKind,
|
||||
OffsetDateTime startedAt,
|
||||
OffsetDateTime endedAt,
|
||||
long durationSeconds,
|
||||
List<String> sourceIntervalIds,
|
||||
boolean synthetic,
|
||||
boolean clippedToRequestedPeriod,
|
||||
String level
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public record TachographEsperDrivingInterruptionIntervalEvent(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
OffsetDateTime startedAt,
|
||||
OffsetDateTime endedAt,
|
||||
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
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public record TachographEsperVehicleUsageIntervalEvent(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
String intervalId,
|
||||
OffsetDateTime startedAt,
|
||||
OffsetDateTime endedAt,
|
||||
long durationSeconds,
|
||||
Long odometerBeginKm,
|
||||
Long odometerEndKm,
|
||||
String registrationKey,
|
||||
String vehicleKey,
|
||||
String sourceKind,
|
||||
List<String> sourceIntervalIds
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package at.procon.eventhub.tachographfilesession.model;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public record TachographEsperVuCardAbsentIntervalEvent(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
OffsetDateTime startedAt,
|
||||
OffsetDateTime endedAt,
|
||||
long durationSeconds,
|
||||
String previousUsageIntervalId,
|
||||
String nextUsageIntervalId,
|
||||
String previousRegistrationKey,
|
||||
String nextRegistrationKey,
|
||||
String previousVehicleKey,
|
||||
String nextVehicleKey
|
||||
) {
|
||||
}
|
||||
|
|
@ -1,5 +1,15 @@
|
|||
package at.procon.eventhub.tachographfilesession.service;
|
||||
|
||||
import com.espertech.esper.common.client.EPCompiled;
|
||||
import com.espertech.esper.common.client.EventBean;
|
||||
import com.espertech.esper.common.client.configuration.Configuration;
|
||||
import com.espertech.esper.compiler.client.CompilerArguments;
|
||||
import com.espertech.esper.compiler.client.EPCompileException;
|
||||
import com.espertech.esper.compiler.client.EPCompilerProvider;
|
||||
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.tachographfilesession.model.DriverExtractionSession;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval;
|
||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
|
||||
|
|
@ -8,22 +18,60 @@ import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
|
|||
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.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;
|
||||
import java.time.Duration;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
@Component
|
||||
public class DriverTimelineBuilder {
|
||||
|
||||
private static final AtomicLong RUNTIME_COUNTER = new AtomicLong();
|
||||
private static final String ACTIVITY_INTERVAL_EVENTS_EPL =
|
||||
loadResource("esper/tachograph-activity-interval-events.epl");
|
||||
private static final String DRIVING_INTERVAL_EVENTS_EPL =
|
||||
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 DAILY_WEEKLY_REST_CANDIDATE_INTERVAL_EVENTS_EPL_TEMPLATE =
|
||||
loadResource("esper/tachograph-daily-weekly-rest-candidate-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 =
|
||||
loadResource("esper/tachograph-vu-card-absent-interval-events.epl");
|
||||
|
||||
public ResolvedDriverTimeline build(TachographFileSession session, DriverExtractionSession driverSession) {
|
||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = mergeVehicleUsageIntervals(driverSession.cardVehicleUsageIntervals(), sourceKind);
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = mergeVehicleUsageIntervals(
|
||||
session.sessionId(),
|
||||
driverSession.driverKey(),
|
||||
driverSession.cardVehicleUsageIntervals(),
|
||||
sourceKind
|
||||
);
|
||||
List<ResolvedActivityInterval> activityIntervals = resolveActivities(driverSession.cardActivityIntervals(), sourceKind);
|
||||
List<ExtractedSupportEvent> supportEvents = driverSession.supportEvents().stream()
|
||||
.sorted(Comparator.comparing(ExtractedSupportEvent::occurredAt)
|
||||
|
|
@ -44,7 +92,360 @@ public class DriverTimelineBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
public List<TachographEsperActivityIntervalEvent> buildEsperActivityIntervalEvents(
|
||||
TachographFileSession session,
|
||||
DriverExtractionSession driverSession
|
||||
) {
|
||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
||||
List<ResolvedActivityInterval> activityIntervals = resolveActivities(driverSession.cardActivityIntervals(), sourceKind);
|
||||
return buildEsperActivityIntervalEvents(session.sessionId(), driverSession.driverKey(), activityIntervals);
|
||||
}
|
||||
|
||||
public List<TachographEsperActivityIntervalEvent> buildEsperActivityIntervalEvents(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
ResolvedDriverTimeline timeline
|
||||
) {
|
||||
return timeline == null
|
||||
? List.of()
|
||||
: buildEsperActivityIntervalEvents(sessionId, driverKey, timeline.activityIntervals());
|
||||
}
|
||||
|
||||
public List<TachographEsperActivityIntervalEvent> buildEsperDrivingIntervalEvents(
|
||||
TachographFileSession session,
|
||||
DriverExtractionSession driverSession
|
||||
) {
|
||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
||||
List<ResolvedActivityInterval> activityIntervals = resolveActivities(driverSession.cardActivityIntervals(), sourceKind);
|
||||
return buildEsperDrivingIntervalEvents(session.sessionId(), driverSession.driverKey(), activityIntervals);
|
||||
}
|
||||
|
||||
public List<TachographEsperActivityIntervalEvent> buildEsperDrivingIntervalEvents(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
ResolvedDriverTimeline timeline
|
||||
) {
|
||||
if (timeline == null) {
|
||||
return List.of();
|
||||
}
|
||||
return buildEsperDrivingIntervalEvents(sessionId, driverKey, timeline.activityIntervals());
|
||||
}
|
||||
|
||||
public List<TachographEsperVehicleUsageIntervalEvent> buildEsperVehicleUsageIntervalEvents(
|
||||
TachographFileSession session,
|
||||
DriverExtractionSession driverSession
|
||||
) {
|
||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = mergeVehicleUsageIntervals(
|
||||
session.sessionId(),
|
||||
driverSession.driverKey(),
|
||||
driverSession.cardVehicleUsageIntervals(),
|
||||
sourceKind
|
||||
);
|
||||
return buildEsperVehicleUsageIntervalEvents(vehicleUsageIntervals);
|
||||
}
|
||||
|
||||
public List<TachographEsperVehicleUsageIntervalEvent> buildEsperVehicleUsageIntervalEvents(
|
||||
ResolvedDriverTimeline timeline
|
||||
) {
|
||||
return timeline == null ? List.of() : buildEsperVehicleUsageIntervalEvents(timeline.vehicleUsageIntervals());
|
||||
}
|
||||
|
||||
public List<TachographEsperDrivingInterruptionIntervalEvent> buildEsperDrivingInterruptionIntervalEvents(
|
||||
TachographFileSession session,
|
||||
DriverExtractionSession driverSession,
|
||||
int significantDrivingMinutes
|
||||
) {
|
||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
||||
List<ResolvedActivityInterval> activityIntervals = resolveActivities(driverSession.cardActivityIntervals(), sourceKind);
|
||||
return buildEsperDrivingInterruptionIntervalEvents(
|
||||
session.sessionId(),
|
||||
driverSession.driverKey(),
|
||||
activityIntervals,
|
||||
significantDrivingMinutes
|
||||
);
|
||||
}
|
||||
|
||||
public List<TachographEsperDrivingInterruptionIntervalEvent> buildEsperDrivingInterruptionIntervalEvents(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
ResolvedDriverTimeline timeline,
|
||||
int significantDrivingMinutes
|
||||
) {
|
||||
if (timeline == null) {
|
||||
return List.of();
|
||||
}
|
||||
return buildEsperDrivingInterruptionIntervalEvents(
|
||||
sessionId,
|
||||
driverKey,
|
||||
timeline.activityIntervals(),
|
||||
significantDrivingMinutes
|
||||
);
|
||||
}
|
||||
|
||||
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> drivingInterruptionVehicleChangeIntervals,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals
|
||||
) {
|
||||
if (drivingInterruptionVehicleChangeIntervals == null
|
||||
|| drivingInterruptionVehicleChangeIntervals.isEmpty()
|
||||
|| vuCardAbsentIntervals == null
|
||||
|| vuCardAbsentIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> {
|
||||
configuration.getCommon().addEventType(
|
||||
"TachographDrivingInterruptionVehicleChangeIntervalInputEvent",
|
||||
drivingInterruptionIntervalInputDefinition()
|
||||
);
|
||||
configuration.getCommon().addEventType(
|
||||
"TachographVuCardAbsentIntervalInputEvent",
|
||||
vuCardAbsentIntervalInputDefinition()
|
||||
);
|
||||
},
|
||||
POTENTIAL_HOME_OVERNIGHT_STAY_INTERVAL_EVENTS_EPL_TEMPLATE,
|
||||
"potentialHomeOvernightStayIntervals",
|
||||
newData -> collectPotentialHomeOvernightStayIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (TachographEsperVuCardAbsentIntervalEvent interval : vuCardAbsentIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toVuCardAbsentIntervalInputMap(interval),
|
||||
"TachographVuCardAbsentIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
for (TachographEsperDrivingInterruptionIntervalEvent interval : drivingInterruptionVehicleChangeIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toDrivingInterruptionIntervalInputMap(interval),
|
||||
"TachographDrivingInterruptionVehicleChangeIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<TachographEsperDrivingInterruptionIntervalEvent> buildEsperDailyWeeklyRestCandidateIntervalEvents(
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||
int minimumRestPeriodMinutes
|
||||
) {
|
||||
if (drivingInterruptionIntervals == null || drivingInterruptionIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographDrivingInterruptionIntervalInputEvent",
|
||||
drivingInterruptionIntervalInputDefinition()
|
||||
),
|
||||
renderDailyWeeklyRestCandidateIntervalEventsEpl(minimumRestPeriodMinutes),
|
||||
"dailyWeeklyRestCandidateIntervals",
|
||||
newData -> collectDrivingInterruptionIntervalEventsFromTimestamps(newData, result),
|
||||
runtime -> {
|
||||
for (TachographEsperDrivingInterruptionIntervalEvent interval : drivingInterruptionIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toDrivingInterruptionIntervalInputMap(interval),
|
||||
"TachographDrivingInterruptionIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<TachographEsperVuCardAbsentIntervalEvent> buildEsperVuCardAbsentIntervalEvents(
|
||||
TachographFileSession session,
|
||||
DriverExtractionSession driverSession
|
||||
) {
|
||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = mergeVehicleUsageIntervals(
|
||||
session.sessionId(),
|
||||
driverSession.driverKey(),
|
||||
driverSession.cardVehicleUsageIntervals(),
|
||||
sourceKind
|
||||
);
|
||||
return buildEsperVuCardAbsentIntervalEvents(vehicleUsageIntervals);
|
||||
}
|
||||
|
||||
public List<TachographEsperVuCardAbsentIntervalEvent> buildEsperVuCardAbsentIntervalEvents(
|
||||
ResolvedDriverTimeline timeline
|
||||
) {
|
||||
return timeline == null ? List.of() : buildEsperVuCardAbsentIntervalEvents(timeline.vehicleUsageIntervals());
|
||||
}
|
||||
|
||||
private List<TachographEsperActivityIntervalEvent> buildEsperActivityIntervalEvents(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
List<ResolvedActivityInterval> activityIntervals
|
||||
) {
|
||||
if (activityIntervals == null || activityIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperActivityIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographActivityIntervalInputEvent",
|
||||
activityIntervalInputDefinition()
|
||||
),
|
||||
ACTIVITY_INTERVAL_EVENTS_EPL,
|
||||
"activityIntervals",
|
||||
newData -> collectActivityIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (ResolvedActivityInterval interval : activityIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toActivityIntervalInputMap(sessionId, driverKey, interval),
|
||||
"TachographActivityIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<TachographEsperActivityIntervalEvent> buildEsperDrivingIntervalEvents(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
List<ResolvedActivityInterval> activityIntervals
|
||||
) {
|
||||
if (activityIntervals == null || activityIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperActivityIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographActivityIntervalInputEvent",
|
||||
activityIntervalInputDefinition()
|
||||
),
|
||||
DRIVING_INTERVAL_EVENTS_EPL,
|
||||
"drivingIntervals",
|
||||
newData -> collectActivityIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (ResolvedActivityInterval interval : activityIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toActivityIntervalInputMap(sessionId, driverKey, interval),
|
||||
"TachographActivityIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<TachographEsperVehicleUsageIntervalEvent> buildEsperVehicleUsageIntervalEvents(
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals
|
||||
) {
|
||||
if (vehicleUsageIntervals == null || vehicleUsageIntervals.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperVehicleUsageIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographVehicleUsageIntervalInputEvent",
|
||||
vehicleUsageIntervalInputDefinition()
|
||||
),
|
||||
VEHICLE_USAGE_INTERVAL_EVENTS_EPL,
|
||||
"vehicleUsageIntervals",
|
||||
newData -> collectVehicleUsageIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toVehicleUsageIntervalInputMap(interval),
|
||||
"TachographVehicleUsageIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<TachographEsperDrivingInterruptionIntervalEvent> buildEsperDrivingInterruptionIntervalEvents(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
List<ResolvedActivityInterval> activityIntervals,
|
||||
int significantDrivingMinutes
|
||||
) {
|
||||
if (activityIntervals == null || activityIntervals.size() < 2) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographActivityIntervalInputEvent",
|
||||
activityIntervalInputDefinition()
|
||||
),
|
||||
renderDrivingInterruptionIntervalEventsEpl(significantDrivingMinutes),
|
||||
"drivingInterruptionIntervals",
|
||||
newData -> collectDrivingInterruptionIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (ResolvedActivityInterval interval : activityIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toActivityIntervalInputMap(sessionId, driverKey, interval),
|
||||
"TachographActivityIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<TachographEsperVuCardAbsentIntervalEvent> buildEsperVuCardAbsentIntervalEvents(
|
||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals
|
||||
) {
|
||||
if (vehicleUsageIntervals == null || vehicleUsageIntervals.size() < 2) {
|
||||
return List.of();
|
||||
}
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> result = new ArrayList<>();
|
||||
executeWithRuntime(
|
||||
configuration -> configuration.getCommon().addEventType(
|
||||
"TachographVehicleUsageIntervalInputEvent",
|
||||
vehicleUsageIntervalInputDefinition()
|
||||
),
|
||||
VU_CARD_ABSENT_INTERVAL_EVENTS_EPL,
|
||||
"vuCardAbsentIntervals",
|
||||
newData -> collectVuCardAbsentIntervalEvents(newData, result),
|
||||
runtime -> {
|
||||
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
||||
runtime.getEventService().sendEventMap(
|
||||
toVehicleUsageIntervalInputMap(interval),
|
||||
"TachographVehicleUsageIntervalInputEvent"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ResolvedVehicleUsageInterval> mergeVehicleUsageIntervals(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
List<ExtractedCardVehicleUsageInterval> rawIntervals,
|
||||
String sourceKind
|
||||
) {
|
||||
|
|
@ -54,6 +455,8 @@ public class DriverTimelineBuilder {
|
|||
List<ResolvedVehicleUsageInterval> sorted = rawIntervals.stream()
|
||||
.filter(interval -> interval.from() != null && (interval.to() == null || interval.to().isAfter(interval.from())))
|
||||
.map(interval -> ResolvedVehicleUsageInterval.resolved(
|
||||
sessionId,
|
||||
driverKey,
|
||||
interval.intervalId(),
|
||||
interval.from(),
|
||||
interval.to(),
|
||||
|
|
@ -79,6 +482,8 @@ public class DriverTimelineBuilder {
|
|||
if (canMerge(current, next)) {
|
||||
currentSources.addAll(next.sourceIntervalIds());
|
||||
current = ResolvedVehicleUsageInterval.resolved(
|
||||
current.sessionId(),
|
||||
current.driverKey(),
|
||||
current.intervalId() + "+" + next.intervalId(),
|
||||
current.from(),
|
||||
mergedTo(current.to(), next.to()),
|
||||
|
|
@ -220,4 +625,417 @@ public class DriverTimelineBuilder {
|
|||
private OffsetDateTime mergeBoundary(OffsetDateTime endInclusive) {
|
||||
return endInclusive == null ? OffsetDateTime.MAX : endInclusive.plusSeconds(1);
|
||||
}
|
||||
|
||||
private void executeWithRuntime(
|
||||
Consumer<Configuration> configurationSetup,
|
||||
String epl,
|
||||
String statementName,
|
||||
Consumer<EventBean[]> listener,
|
||||
Consumer<EPRuntime> sender
|
||||
) {
|
||||
EPRuntime runtime = null;
|
||||
try {
|
||||
Configuration configuration = new Configuration();
|
||||
configurationSetup.accept(configuration);
|
||||
String runtimeUri = "eventhub-tachograph-projection-" + RUNTIME_COUNTER.incrementAndGet();
|
||||
runtime = EPRuntimeProvider.getRuntime(runtimeUri, configuration);
|
||||
|
||||
CompilerArguments arguments = new CompilerArguments(configuration);
|
||||
EPCompiled compiled = EPCompilerProvider.getCompiler().compile(epl, arguments);
|
||||
EPDeployment deployment = runtime.getDeploymentService().deploy(compiled);
|
||||
runtime.getDeploymentService()
|
||||
.getStatement(deployment.getDeploymentId(), statementName)
|
||||
.addListener((newData, oldData, statement, rt) -> listener.accept(newData));
|
||||
|
||||
sender.accept(runtime);
|
||||
} catch (EPCompileException | EPDeployException e) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot compile/deploy tachograph projection EPL for statement '" + statementName + "'",
|
||||
e
|
||||
);
|
||||
} finally {
|
||||
if (runtime != null) {
|
||||
runtime.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> activityIntervalInputDefinition() {
|
||||
Map<String, Object> definition = new LinkedHashMap<>();
|
||||
definition.put("sessionId", UUID.class);
|
||||
definition.put("driverKey", String.class);
|
||||
definition.put("intervalId", String.class);
|
||||
definition.put("activityType", String.class);
|
||||
definition.put("cardSlot", String.class);
|
||||
definition.put("cardStatus", String.class);
|
||||
definition.put("drivingStatus", String.class);
|
||||
definition.put("registrationKey", String.class);
|
||||
definition.put("vehicleKey", String.class);
|
||||
definition.put("sourceKind", String.class);
|
||||
definition.put("firstSourceIntervalId", String.class);
|
||||
definition.put("lastSourceIntervalId", 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("sourceIntervalIds", java.util.List.class);
|
||||
definition.put("synthetic", boolean.class);
|
||||
definition.put("clippedToRequestedPeriod", boolean.class);
|
||||
definition.put("level", String.class);
|
||||
return definition;
|
||||
}
|
||||
|
||||
private Map<String, Object> vehicleUsageIntervalInputDefinition() {
|
||||
Map<String, Object> definition = new LinkedHashMap<>();
|
||||
definition.put("sessionId", UUID.class);
|
||||
definition.put("driverKey", String.class);
|
||||
definition.put("intervalId", String.class);
|
||||
definition.put("firstSourceIntervalId", String.class);
|
||||
definition.put("lastSourceIntervalId", 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("odometerBeginKm", Long.class);
|
||||
definition.put("odometerEndKm", Long.class);
|
||||
definition.put("registrationKey", String.class);
|
||||
definition.put("vehicleKey", String.class);
|
||||
definition.put("sourceKind", String.class);
|
||||
definition.put("sourceIntervalIds", java.util.List.class);
|
||||
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,
|
||||
ResolvedActivityInterval interval
|
||||
) {
|
||||
Map<String, Object> event = new LinkedHashMap<>();
|
||||
event.put("sessionId", sessionId);
|
||||
event.put("driverKey", driverKey);
|
||||
event.put("intervalId", interval.intervalId());
|
||||
event.put("activityType", interval.activityType());
|
||||
event.put("cardSlot", interval.slot());
|
||||
event.put("cardStatus", interval.cardStatus());
|
||||
event.put("drivingStatus", interval.drivingStatus());
|
||||
event.put("registrationKey", interval.registrationKey());
|
||||
event.put("vehicleKey", interval.vehicleKey());
|
||||
event.put("sourceKind", interval.sourceKind());
|
||||
event.put("firstSourceIntervalId", firstSourceIntervalId(interval));
|
||||
event.put("lastSourceIntervalId", lastSourceIntervalId(interval));
|
||||
event.put("startedAt", interval.from());
|
||||
event.put("endedAt", interval.to());
|
||||
event.put("startedAtEpochSecond", interval.from().toEpochSecond());
|
||||
event.put("endedAtEpochSecond", interval.to().toEpochSecond());
|
||||
event.put("durationSeconds", interval.durationSeconds());
|
||||
event.put("sourceIntervalIds", interval.sourceIntervalIds());
|
||||
event.put("synthetic", interval.synthetic());
|
||||
event.put("clippedToRequestedPeriod", interval.clippedToRequestedPeriod());
|
||||
event.put("level", interval.level());
|
||||
return event;
|
||||
}
|
||||
|
||||
private String firstSourceIntervalId(ResolvedActivityInterval interval) {
|
||||
return interval.sourceIntervalIds().isEmpty() ? interval.intervalId() : interval.sourceIntervalIds().get(0);
|
||||
}
|
||||
|
||||
private String lastSourceIntervalId(ResolvedActivityInterval interval) {
|
||||
return interval.sourceIntervalIds().isEmpty()
|
||||
? interval.intervalId()
|
||||
: interval.sourceIntervalIds().get(interval.sourceIntervalIds().size() - 1);
|
||||
}
|
||||
|
||||
private Map<String, Object> toVehicleUsageIntervalInputMap(ResolvedVehicleUsageInterval interval) {
|
||||
Map<String, Object> event = new LinkedHashMap<>();
|
||||
event.put("sessionId", interval.sessionId());
|
||||
event.put("driverKey", interval.driverKey());
|
||||
event.put("intervalId", interval.intervalId());
|
||||
event.put("firstSourceIntervalId", firstSourceIntervalId(interval));
|
||||
event.put("lastSourceIntervalId", lastSourceIntervalId(interval));
|
||||
event.put("startedAt", interval.from());
|
||||
event.put("endedAt", interval.to());
|
||||
event.put("startedAtEpochSecond", interval.from().toEpochSecond());
|
||||
event.put("endedAtEpochSecond", interval.to() == null ? null : interval.to().toEpochSecond());
|
||||
event.put("durationSeconds", interval.durationSeconds());
|
||||
event.put("odometerBeginKm", interval.odometerBeginKm());
|
||||
event.put("odometerEndKm", interval.odometerEndKm());
|
||||
event.put("registrationKey", interval.registrationKey());
|
||||
event.put("vehicleKey", interval.vehicleKey());
|
||||
event.put("sourceKind", interval.sourceKind());
|
||||
event.put("sourceIntervalIds", interval.sourceIntervalIds());
|
||||
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);
|
||||
}
|
||||
|
||||
private String lastSourceIntervalId(ResolvedVehicleUsageInterval interval) {
|
||||
return interval.sourceIntervalIds().isEmpty()
|
||||
? interval.intervalId()
|
||||
: interval.sourceIntervalIds().get(interval.sourceIntervalIds().size() - 1);
|
||||
}
|
||||
|
||||
private void collectActivityIntervalEvents(
|
||||
EventBean[] newData,
|
||||
List<TachographEsperActivityIntervalEvent> target
|
||||
) {
|
||||
if (newData == null) {
|
||||
return;
|
||||
}
|
||||
for (EventBean event : newData) {
|
||||
target.add(new TachographEsperActivityIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
(String) event.get("intervalId"),
|
||||
(String) event.get("activityType"),
|
||||
(String) event.get("cardSlot"),
|
||||
(String) event.get("cardStatus"),
|
||||
(String) event.get("drivingStatus"),
|
||||
(String) event.get("registrationKey"),
|
||||
(String) event.get("vehicleKey"),
|
||||
(String) event.get("sourceKind"),
|
||||
(OffsetDateTime) event.get("startedAt"),
|
||||
(OffsetDateTime) event.get("endedAt"),
|
||||
(Long) event.get("durationSeconds"),
|
||||
castSourceIntervalIds(event.get("sourceIntervalIds")),
|
||||
(Boolean) event.get("synthetic"),
|
||||
(Boolean) event.get("clippedToRequestedPeriod"),
|
||||
(String) event.get("level")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void collectVehicleUsageIntervalEvents(
|
||||
EventBean[] newData,
|
||||
List<TachographEsperVehicleUsageIntervalEvent> target
|
||||
) {
|
||||
if (newData == null) {
|
||||
return;
|
||||
}
|
||||
for (EventBean event : newData) {
|
||||
target.add(new TachographEsperVehicleUsageIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
(String) event.get("intervalId"),
|
||||
(OffsetDateTime) event.get("startedAt"),
|
||||
(OffsetDateTime) event.get("endedAt"),
|
||||
(Long) event.get("durationSeconds"),
|
||||
(Long) event.get("odometerBeginKm"),
|
||||
(Long) event.get("odometerEndKm"),
|
||||
(String) event.get("registrationKey"),
|
||||
(String) event.get("vehicleKey"),
|
||||
(String) event.get("sourceKind"),
|
||||
castSourceIntervalIds(event.get("sourceIntervalIds"))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDrivingInterruptionIntervalEvents(
|
||||
EventBean[] newData,
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> target
|
||||
) {
|
||||
if (newData == null) {
|
||||
return;
|
||||
}
|
||||
for (EventBean event : newData) {
|
||||
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||
target.add(new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
OffsetDateTime.ofInstant(Instant.ofEpochSecond(startedAtEpochSecond), ZoneOffset.UTC),
|
||||
OffsetDateTime.ofInstant(Instant.ofEpochSecond(endedAtEpochSecond), ZoneOffset.UTC),
|
||||
(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")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private void collectVuCardAbsentIntervalEvents(
|
||||
EventBean[] newData,
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> target
|
||||
) {
|
||||
if (newData == null) {
|
||||
return;
|
||||
}
|
||||
for (EventBean event : newData) {
|
||||
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||
target.add(new TachographEsperVuCardAbsentIntervalEvent(
|
||||
(UUID) event.get("sessionId"),
|
||||
(String) event.get("driverKey"),
|
||||
OffsetDateTime.ofInstant(Instant.ofEpochSecond(startedAtEpochSecond), ZoneOffset.UTC),
|
||||
OffsetDateTime.ofInstant(Instant.ofEpochSecond(endedAtEpochSecond), ZoneOffset.UTC),
|
||||
(Long) event.get("durationSeconds"),
|
||||
(String) event.get("previousUsageIntervalId"),
|
||||
(String) event.get("nextUsageIntervalId"),
|
||||
(String) event.get("previousRegistrationKey"),
|
||||
(String) event.get("nextRegistrationKey"),
|
||||
(String) event.get("previousVehicleKey"),
|
||||
(String) event.get("nextVehicleKey")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static String loadResource(String path) {
|
||||
try {
|
||||
ClassPathResource resource = new ClassPathResource(path);
|
||||
return StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot load EPL resource: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String renderDrivingInterruptionIntervalEventsEpl(int significantDrivingMinutes) {
|
||||
long thresholdSeconds = Math.max(1, significantDrivingMinutes) * 60L;
|
||||
return DRIVING_INTERRUPTION_INTERVAL_EVENTS_EPL_TEMPLATE.replace(
|
||||
"${SIGNIFICANT_DRIVING_THRESHOLD_SECONDS}",
|
||||
Long.toString(thresholdSeconds)
|
||||
);
|
||||
}
|
||||
|
||||
private String renderDailyWeeklyRestCandidateIntervalEventsEpl(int minimumRestPeriodMinutes) {
|
||||
long thresholdSeconds = Math.max(1, minimumRestPeriodMinutes) * 60L;
|
||||
return DAILY_WEEKLY_REST_CANDIDATE_INTERVAL_EVENTS_EPL_TEMPLATE.replace(
|
||||
"${MINIMUM_REST_PERIOD_THRESHOLD_SECONDS}",
|
||||
Long.toString(thresholdSeconds)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package at.procon.eventhub.tachographfilesession.service;
|
||||
|
||||
import at.procon.eventhub.config.EventHubProperties;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperEventsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||
|
|
@ -10,7 +12,12 @@ import at.procon.eventhub.tachographfilesession.model.ProcessedOperatingPeriod;
|
|||
import at.procon.eventhub.tachographfilesession.model.ProcessedShiftDrivingEvaluation;
|
||||
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||
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;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -113,6 +120,334 @@ public class TachographFileSessionProcessingService {
|
|||
);
|
||||
}
|
||||
|
||||
public TachographEsperDriverProcessingResultDto getEsperDriverProcessingResults(
|
||||
UUID sessionId,
|
||||
String driverKey
|
||||
) {
|
||||
return getEsperDriverProcessingResults(sessionId, driverKey, null);
|
||||
}
|
||||
|
||||
public TachographEsperDriverProcessingResultDto getEsperDriverProcessingResults(
|
||||
UUID sessionId,
|
||||
String driverKey,
|
||||
TachographEsperEventsProcessingRequest request
|
||||
) {
|
||||
TachographEsperEventsProcessingRequest effectiveRequest = request == null
|
||||
? new TachographEsperEventsProcessingRequest(null, null, null, null)
|
||||
: request;
|
||||
TachographFileSession session = repository.find(sessionId)
|
||||
.orElseThrow(() -> new TachographFileSessionNotFoundException(sessionId));
|
||||
DriverExtractionSession driver = session.driversByKey().get(driverKey);
|
||||
if (driver == null) {
|
||||
throw new DriverNotFoundInSessionException(sessionId, driverKey);
|
||||
}
|
||||
|
||||
ResolvedDriverTimeline timeline = driverTimelineBuilder.build(session, driver);
|
||||
OffsetDateTime requestedFrom = effectiveRequest.occurredFrom() == null ? timeline.loadedFrom() : utc(effectiveRequest.occurredFrom());
|
||||
OffsetDateTime requestedTo = effectiveRequest.occurredTo() == null ? timeline.loadedTo() : utc(effectiveRequest.occurredTo());
|
||||
if (requestedFrom != null && requestedTo != null && requestedTo.isBefore(requestedFrom)) {
|
||||
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),
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperActivityIntervalEvent> drivingIntervals = clipEsperActivityIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperDrivingIntervalEvents(sessionId, driverKey, timeline),
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionIntervals =
|
||||
driverTimelineBuilder.buildEsperDrivingInterruptionIntervalEvents(
|
||||
sessionId,
|
||||
driverKey,
|
||||
timeline,
|
||||
significantDrivingMinutes
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals =
|
||||
clipEsperDrivingInterruptionIntervalEvents(
|
||||
rawDrivingInterruptionIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDailyWeeklyRestCandidateIntervals =
|
||||
driverTimelineBuilder.buildEsperDailyWeeklyRestCandidateIntervalEvents(
|
||||
rawDrivingInterruptionIntervals,
|
||||
minimumRestPeriodMinutes
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals =
|
||||
clipEsperDrivingInterruptionIntervalEvents(
|
||||
rawDailyWeeklyRestCandidateIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionVehicleChangeIntervals =
|
||||
driverTimelineBuilder.buildEsperDrivingInterruptionVehicleChangeIntervalEvents(
|
||||
rawDailyWeeklyRestCandidateIntervals
|
||||
);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals =
|
||||
clipEsperDrivingInterruptionIntervalEvents(
|
||||
rawDrivingInterruptionVehicleChangeIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals =
|
||||
driverTimelineBuilder.buildEsperVuCardAbsentIntervalEvents(timeline);
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals =
|
||||
clipEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
rawDrivingInterruptionVehicleChangeIntervals,
|
||||
rawVuCardAbsentIntervals
|
||||
),
|
||||
rawVuCardAbsentIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals = clipEsperVehicleUsageIntervalEvents(
|
||||
driverTimelineBuilder.buildEsperVehicleUsageIntervalEvents(timeline),
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals = clipEsperVuCardAbsentIntervalEvents(
|
||||
rawVuCardAbsentIntervals,
|
||||
requestedFrom,
|
||||
requestedTo
|
||||
);
|
||||
|
||||
return new TachographEsperDriverProcessingResultDto(
|
||||
sessionId,
|
||||
driverKey,
|
||||
timeline.sourceKind(),
|
||||
timeline.loadedFrom(),
|
||||
timeline.loadedTo(),
|
||||
requestedFrom,
|
||||
requestedTo,
|
||||
activityIntervals.size(),
|
||||
drivingIntervals.size(),
|
||||
drivingInterruptionIntervals.size(),
|
||||
drivingInterruptionVehicleChangeIntervals.size(),
|
||||
dailyWeeklyRestCandidateIntervals.size(),
|
||||
potentialHomeOvernightStayIntervals.size(),
|
||||
vehicleUsageIntervals.size(),
|
||||
vuCardAbsentIntervals.size(),
|
||||
activityIntervals,
|
||||
drivingIntervals,
|
||||
drivingInterruptionIntervals,
|
||||
drivingInterruptionVehicleChangeIntervals,
|
||||
dailyWeeklyRestCandidateIntervals,
|
||||
potentialHomeOvernightStayIntervals,
|
||||
vehicleUsageIntervals,
|
||||
vuCardAbsentIntervals,
|
||||
esperProjectionNotes()
|
||||
);
|
||||
}
|
||||
|
||||
private List<TachographEsperActivityIntervalEvent> clipEsperActivityIntervalEvents(
|
||||
List<TachographEsperActivityIntervalEvent> intervals,
|
||||
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;
|
||||
}
|
||||
boolean clipped = interval.clippedToRequestedPeriod()
|
||||
|| !start.equals(interval.startedAt())
|
||||
|| !end.equals(interval.endedAt());
|
||||
return new TachographEsperActivityIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
interval.intervalId(),
|
||||
interval.activityType(),
|
||||
interval.cardSlot(),
|
||||
interval.cardStatus(),
|
||||
interval.drivingStatus(),
|
||||
interval.registrationKey(),
|
||||
interval.vehicleKey(),
|
||||
interval.sourceKind(),
|
||||
start,
|
||||
end,
|
||||
Duration.between(start, end).getSeconds(),
|
||||
interval.sourceIntervalIds(),
|
||||
interval.synthetic(),
|
||||
clipped,
|
||||
interval.level()
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(TachographEsperActivityIntervalEvent::startedAt)
|
||||
.thenComparing(TachographEsperActivityIntervalEvent::endedAt)
|
||||
.thenComparing(TachographEsperActivityIntervalEvent::activityType, Comparator.nullsLast(String::compareTo)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<TachographEsperVehicleUsageIntervalEvent> clipEsperVehicleUsageIntervalEvents(
|
||||
List<TachographEsperVehicleUsageIntervalEvent> intervals,
|
||||
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;
|
||||
}
|
||||
boolean startClipped = !start.equals(interval.startedAt());
|
||||
boolean endClipped = !end.equals(interval.endedAt());
|
||||
return new TachographEsperVehicleUsageIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
interval.intervalId(),
|
||||
start,
|
||||
end,
|
||||
Duration.between(start, end).getSeconds(),
|
||||
startClipped ? null : interval.odometerBeginKm(),
|
||||
endClipped ? null : interval.odometerEndKm(),
|
||||
interval.registrationKey(),
|
||||
interval.vehicleKey(),
|
||||
interval.sourceKind(),
|
||||
interval.sourceIntervalIds()
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(TachographEsperVehicleUsageIntervalEvent::startedAt)
|
||||
.thenComparing(TachographEsperVehicleUsageIntervalEvent::endedAt)
|
||||
.thenComparing(TachographEsperVehicleUsageIntervalEvent::intervalId, Comparator.nullsLast(String::compareTo)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<TachographEsperDrivingInterruptionIntervalEvent> clipEsperDrivingInterruptionIntervalEvents(
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> intervals,
|
||||
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;
|
||||
}
|
||||
return new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
start,
|
||||
end,
|
||||
Duration.between(start, end).getSeconds(),
|
||||
interval.previousDrivingSourceIntervalId(),
|
||||
interval.nextDrivingSourceIntervalId(),
|
||||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(TachographEsperDrivingInterruptionIntervalEvent::startedAt)
|
||||
.thenComparing(TachographEsperDrivingInterruptionIntervalEvent::endedAt))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<TachographEsperVuCardAbsentIntervalEvent> clipEsperVuCardAbsentIntervalEvents(
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> intervals,
|
||||
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;
|
||||
}
|
||||
return new TachographEsperVuCardAbsentIntervalEvent(
|
||||
interval.sessionId(),
|
||||
interval.driverKey(),
|
||||
start,
|
||||
end,
|
||||
Duration.between(start, end).getSeconds(),
|
||||
interval.previousUsageIntervalId(),
|
||||
interval.nextUsageIntervalId(),
|
||||
interval.previousRegistrationKey(),
|
||||
interval.nextRegistrationKey(),
|
||||
interval.previousVehicleKey(),
|
||||
interval.nextVehicleKey()
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(Comparator.comparing(TachographEsperVuCardAbsentIntervalEvent::startedAt)
|
||||
.thenComparing(TachographEsperVuCardAbsentIntervalEvent::endedAt))
|
||||
.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
|
||||
|
|
@ -651,6 +986,18 @@ public class TachographFileSessionProcessingService {
|
|||
: request.gapDetectionToleranceSeconds();
|
||||
}
|
||||
|
||||
private int resolveEsperSignificantDrivingMinutes(TachographEsperEventsProcessingRequest request) {
|
||||
return request.significantDrivingMinutes() == null
|
||||
? properties.getTachographFileSession().getProcessing().getSignificantDrivingMinutes()
|
||||
: 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.",
|
||||
|
|
@ -660,6 +1007,43 @@ public class TachographFileSessionProcessingService {
|
|||
);
|
||||
}
|
||||
|
||||
private List<String> esperProjectionNotes() {
|
||||
return List.of(
|
||||
"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 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.",
|
||||
"Potential home overnight stay intervals are vehicle-change daily/weekly rest candidates where VU card-absent overlap covers at least 95% of the candidate interval.",
|
||||
"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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
@name('activityIntervals')
|
||||
select
|
||||
sessionId,
|
||||
driverKey,
|
||||
intervalId,
|
||||
activityType,
|
||||
cardSlot,
|
||||
cardStatus,
|
||||
drivingStatus,
|
||||
registrationKey,
|
||||
vehicleKey,
|
||||
sourceKind,
|
||||
startedAt,
|
||||
endedAt,
|
||||
durationSeconds,
|
||||
sourceIntervalIds,
|
||||
synthetic,
|
||||
clippedToRequestedPeriod,
|
||||
level
|
||||
from TachographActivityIntervalInputEvent
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@name('dailyWeeklyRestCandidateIntervals')
|
||||
select *
|
||||
from TachographDrivingInterruptionIntervalInputEvent(durationSeconds > ${MINIMUM_REST_PERIOD_THRESHOLD_SECONDS});
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
create schema SignificantDrivingInterval(
|
||||
sessionId java.util.UUID,
|
||||
driverKey string,
|
||||
firstSourceIntervalId string,
|
||||
lastSourceIntervalId string,
|
||||
startedAtEpochSecond long,
|
||||
endedAtEpochSecond long,
|
||||
durationSeconds long,
|
||||
registrationKey string,
|
||||
vehicleKey string
|
||||
);
|
||||
|
||||
create schema DrivingInterruptionInterval(
|
||||
sessionId java.util.UUID,
|
||||
driverKey string,
|
||||
startedAtEpochSecond long,
|
||||
endedAtEpochSecond long,
|
||||
durationSeconds long,
|
||||
previousDrivingSourceIntervalId string,
|
||||
nextDrivingSourceIntervalId string,
|
||||
previousRegistrationKey string,
|
||||
nextRegistrationKey string,
|
||||
previousVehicleKey string,
|
||||
nextVehicleKey string
|
||||
);
|
||||
|
||||
insert into SignificantDrivingInterval
|
||||
select
|
||||
sessionId,
|
||||
driverKey,
|
||||
firstSourceIntervalId,
|
||||
lastSourceIntervalId,
|
||||
startedAtEpochSecond,
|
||||
endedAtEpochSecond,
|
||||
durationSeconds,
|
||||
registrationKey,
|
||||
vehicleKey
|
||||
from TachographActivityIntervalInputEvent(activityType = 'DRIVE', durationSeconds > ${SIGNIFICANT_DRIVING_THRESHOLD_SECONDS});
|
||||
|
||||
create window PreviousSignificantDrivingInterval#unique(driverKey) as SignificantDrivingInterval;
|
||||
|
||||
on SignificantDrivingInterval as next
|
||||
insert into DrivingInterruptionInterval
|
||||
select
|
||||
priorInterval.sessionId as sessionId,
|
||||
priorInterval.driverKey as driverKey,
|
||||
priorInterval.endedAtEpochSecond as startedAtEpochSecond,
|
||||
next.startedAtEpochSecond as endedAtEpochSecond,
|
||||
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
|
||||
where priorInterval.driverKey = next.driverKey
|
||||
and next.startedAtEpochSecond > priorInterval.endedAtEpochSecond;
|
||||
|
||||
@Priority(20)
|
||||
on SignificantDrivingInterval
|
||||
delete from PreviousSignificantDrivingInterval;
|
||||
|
||||
@Priority(10)
|
||||
on SignificantDrivingInterval as current
|
||||
insert into PreviousSignificantDrivingInterval
|
||||
select *;
|
||||
|
||||
@name('drivingInterruptionIntervals')
|
||||
select * from DrivingInterruptionInterval;
|
||||
|
|
@ -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,20 @@
|
|||
@name('drivingIntervals')
|
||||
select
|
||||
sessionId,
|
||||
driverKey,
|
||||
intervalId,
|
||||
activityType,
|
||||
cardSlot,
|
||||
cardStatus,
|
||||
drivingStatus,
|
||||
registrationKey,
|
||||
vehicleKey,
|
||||
sourceKind,
|
||||
startedAt,
|
||||
endedAt,
|
||||
durationSeconds,
|
||||
sourceIntervalIds,
|
||||
synthetic,
|
||||
clippedToRequestedPeriod,
|
||||
level
|
||||
from TachographActivityIntervalInputEvent(activityType = 'DRIVE')
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
@name('potentialHomeOvernightStayIntervals')
|
||||
select
|
||||
c.sessionId as sessionId,
|
||||
c.driverKey as driverKey,
|
||||
c.startedAt as startedAt,
|
||||
c.endedAt as endedAt,
|
||||
c.durationSeconds as durationSeconds,
|
||||
sum(
|
||||
case
|
||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||
then c.durationSeconds
|
||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
||||
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
||||
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
end
|
||||
) as unknownDurationSeconds,
|
||||
(sum(
|
||||
case
|
||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||
then c.durationSeconds
|
||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
||||
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
||||
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
end
|
||||
) * 100.0d) / c.durationSeconds as unknownCoveragePercent,
|
||||
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||
c.previousRegistrationKey as previousRegistrationKey,
|
||||
c.nextRegistrationKey as nextRegistrationKey,
|
||||
c.previousVehicleKey as previousVehicleKey,
|
||||
c.nextVehicleKey as nextVehicleKey
|
||||
from TachographDrivingInterruptionVehicleChangeIntervalInputEvent as c unidirectional,
|
||||
TachographVuCardAbsentIntervalInputEvent#keepall as u
|
||||
where u.driverKey = c.driverKey
|
||||
and u.startedAtEpochSecond < c.endedAtEpochSecond
|
||||
and u.endedAtEpochSecond > c.startedAtEpochSecond
|
||||
group by
|
||||
c.sessionId,
|
||||
c.driverKey,
|
||||
c.startedAt,
|
||||
c.endedAt,
|
||||
c.startedAtEpochSecond,
|
||||
c.endedAtEpochSecond,
|
||||
c.durationSeconds,
|
||||
c.previousDrivingSourceIntervalId,
|
||||
c.nextDrivingSourceIntervalId,
|
||||
c.previousRegistrationKey,
|
||||
c.nextRegistrationKey,
|
||||
c.previousVehicleKey,
|
||||
c.nextVehicleKey
|
||||
having sum(
|
||||
case
|
||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||
then c.durationSeconds
|
||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
||||
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
||||
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||
end
|
||||
) * 100L >= c.durationSeconds * 95L;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
@name('vehicleUsageIntervals')
|
||||
select
|
||||
sessionId,
|
||||
driverKey,
|
||||
intervalId,
|
||||
startedAt,
|
||||
endedAt,
|
||||
durationSeconds,
|
||||
odometerBeginKm,
|
||||
odometerEndKm,
|
||||
registrationKey,
|
||||
vehicleKey,
|
||||
sourceKind,
|
||||
sourceIntervalIds
|
||||
from TachographVehicleUsageIntervalInputEvent
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
create context PerDriver partition by driverKey from TachographVehicleUsageIntervalInputEvent;
|
||||
|
||||
create schema VuCardAbsentInterval(
|
||||
sessionId java.util.UUID,
|
||||
driverKey string,
|
||||
startedAtEpochSecond long,
|
||||
endedAtEpochSecond long,
|
||||
durationSeconds long,
|
||||
previousUsageIntervalId string,
|
||||
nextUsageIntervalId string,
|
||||
previousRegistrationKey string,
|
||||
nextRegistrationKey string,
|
||||
previousVehicleKey string,
|
||||
nextVehicleKey string
|
||||
);
|
||||
|
||||
context PerDriver
|
||||
create window PreviousVehicleUsageInterval#lastevent as TachographVehicleUsageIntervalInputEvent;
|
||||
|
||||
@Priority(30)
|
||||
context PerDriver
|
||||
on TachographVehicleUsageIntervalInputEvent as next
|
||||
insert into VuCardAbsentInterval
|
||||
select
|
||||
priorInterval.sessionId as sessionId,
|
||||
priorInterval.driverKey as driverKey,
|
||||
priorInterval.endedAtEpochSecond + 1L as startedAtEpochSecond,
|
||||
next.startedAtEpochSecond as endedAtEpochSecond,
|
||||
next.startedAtEpochSecond - (priorInterval.endedAtEpochSecond + 1L) as durationSeconds,
|
||||
priorInterval.lastSourceIntervalId as previousUsageIntervalId,
|
||||
next.firstSourceIntervalId as nextUsageIntervalId,
|
||||
priorInterval.registrationKey as previousRegistrationKey,
|
||||
next.registrationKey as nextRegistrationKey,
|
||||
priorInterval.vehicleKey as previousVehicleKey,
|
||||
next.vehicleKey as nextVehicleKey
|
||||
from PreviousVehicleUsageInterval as priorInterval
|
||||
where priorInterval.endedAt is not null
|
||||
and next.startedAt is not null
|
||||
and next.startedAtEpochSecond > priorInterval.endedAtEpochSecond + 1L;
|
||||
|
||||
@Priority(20)
|
||||
context PerDriver
|
||||
on TachographVehicleUsageIntervalInputEvent
|
||||
delete from PreviousVehicleUsageInterval;
|
||||
|
||||
@Priority(10)
|
||||
context PerDriver
|
||||
on TachographVehicleUsageIntervalInputEvent as current
|
||||
insert into PreviousVehicleUsageInterval
|
||||
select *;
|
||||
|
||||
@name('vuCardAbsentIntervals')
|
||||
select * from VuCardAbsentInterval;
|
||||
|
|
@ -9,6 +9,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperEventsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||
|
|
@ -17,8 +19,14 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteR
|
|||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
||||
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;
|
||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
|
@ -58,6 +66,148 @@ class TachographFileSessionControllerTest {
|
|||
when(service.getSession(sessionId)).thenReturn(summary);
|
||||
when(service.listDrivers(sessionId)).thenReturn(new TachographFileSessionListDriversResponse(sessionId, List.of(driver)));
|
||||
when(service.getDriver(sessionId, "12:123")).thenReturn(new TachographFileDriverDetailDto(sessionId, "12:123", null, null, List.of(), List.of(), List.of(), List.of(), List.of(), List.of()));
|
||||
when(processingService.getEsperDriverProcessingResults(
|
||||
eq(sessionId),
|
||||
eq("12:123"),
|
||||
org.mockito.ArgumentMatchers.any(TachographEsperEventsProcessingRequest.class)
|
||||
))
|
||||
.thenReturn(new TachographEsperDriverProcessingResultDto(
|
||||
sessionId,
|
||||
"12:123",
|
||||
"DRIVER_CARD",
|
||||
OffsetDateTime.parse("2026-05-12T08:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T12:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T08:30:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T11:30:00Z"),
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
List.of(new TachographEsperActivityIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
"ACT-1",
|
||||
"WORK",
|
||||
"DRIVER",
|
||||
"INSERTED",
|
||||
"SINGLE",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"DRIVER_CARD",
|
||||
OffsetDateTime.parse("2026-05-12T08:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T09:00:00Z"),
|
||||
3600L,
|
||||
List.of("ACT-1"),
|
||||
false,
|
||||
false,
|
||||
"RAW_INTERVAL"
|
||||
)),
|
||||
List.of(new TachographEsperActivityIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
"ACT-2",
|
||||
"DRIVE",
|
||||
"DRIVER",
|
||||
"INSERTED",
|
||||
"SINGLE",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"DRIVER_CARD",
|
||||
OffsetDateTime.parse("2026-05-12T09:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T10:00:00Z"),
|
||||
3600L,
|
||||
List.of("ACT-2"),
|
||||
false,
|
||||
false,
|
||||
"RAW_INTERVAL"
|
||||
)),
|
||||
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 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 TachographEsperDrivingInterruptionIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-12T10:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T22:00:00Z"),
|
||||
43_200L,
|
||||
"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"
|
||||
)),
|
||||
List.of(new TachographEsperVehicleUsageIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
"CVU-1",
|
||||
OffsetDateTime.parse("2026-05-12T08:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-12T10:00:00Z"),
|
||||
7200L,
|
||||
100L,
|
||||
200L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"DRIVER_CARD",
|
||||
List.of("CVU-1")
|
||||
)),
|
||||
List.of(new TachographEsperVuCardAbsentIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-12T10:00:01Z"),
|
||||
OffsetDateTime.parse("2026-05-12T11:00:00Z"),
|
||||
3599L,
|
||||
"CVU-1",
|
||||
"CVU-2",
|
||||
"12:REG-1",
|
||||
"12:REG-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
)),
|
||||
List.of("note")
|
||||
));
|
||||
when(processingService.evaluateOperatingPeriods(eq(sessionId), eq("12:123"), org.mockito.ArgumentMatchers.any(TachographOperatingPeriodsProcessingRequest.class)))
|
||||
.thenReturn(new TachographOperatingPeriodsProcessingResultDto(
|
||||
sessionId,
|
||||
|
|
@ -104,6 +254,37 @@ class TachographFileSessionControllerTest {
|
|||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.driverKey").value("12:123"));
|
||||
|
||||
mockMvc.perform(post("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers/{driverKey}/processing/esper-events", sessionId, "12:123")
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
{
|
||||
"occurredFrom": "2026-05-12T08:30:00Z",
|
||||
"occurredTo": "2026-05-12T11:30:00Z",
|
||||
"significantDrivingMinutes": 3,
|
||||
"minimumRestPeriodMinutes": 720
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.driverKey").value("12:123"))
|
||||
.andExpect(jsonPath("$.sourceKind").value("DRIVER_CARD"))
|
||||
.andExpect(jsonPath("$.requestedFrom").value("2026-05-12T08:30:00Z"))
|
||||
.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("$.dailyWeeklyRestCandidateIntervalCount").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")
|
||||
.contentType("application/json")
|
||||
.content("""
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
|
|||
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.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;
|
||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
||||
import java.time.Instant;
|
||||
|
|
@ -100,6 +105,8 @@ class DriverTimelineBuilderTest {
|
|||
|
||||
assertThat(timeline.sourceKind()).isEqualTo("DRIVER_CARD");
|
||||
assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).sessionId()).isEqualTo(session.sessionId());
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).driverKey()).isEqualTo("12:123");
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).from()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).to()).isEqualTo(OffsetDateTime.parse("2026-05-02T08:00:00Z"));
|
||||
assertThat(timeline.activityIntervals()).hasSize(1);
|
||||
|
|
@ -169,9 +176,389 @@ class DriverTimelineBuilderTest {
|
|||
ResolvedDriverTimeline timeline = builder.build(session, driver);
|
||||
|
||||
assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).sessionId()).isEqualTo(session.sessionId());
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).driverKey()).isEqualTo("12:123");
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).from()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).to()).isNull();
|
||||
assertThat(timeline.vehicleUsageIntervals().get(0).sourceIntervalIds()).containsExactly("CVU-1", "CVU-2");
|
||||
assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T09:00:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildsEsperActivityAndDrivingIntervalEventsFromResolvedTimeline() {
|
||||
DriverExtractionSession driver = new DriverExtractionSession(
|
||||
"12:123",
|
||||
null,
|
||||
null,
|
||||
List.of(),
|
||||
List.of(),
|
||||
List.of(),
|
||||
List.of(
|
||||
new ExtractedCardActivityInterval(
|
||||
"ACT-1",
|
||||
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||
"WORK",
|
||||
"DRIVER",
|
||||
"INSERTED",
|
||||
"SINGLE",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"a"
|
||||
),
|
||||
new ExtractedCardActivityInterval(
|
||||
"ACT-2",
|
||||
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T10: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, 0, 0, 0, 0),
|
||||
List.of(),
|
||||
Instant.now(),
|
||||
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||
);
|
||||
|
||||
List<TachographEsperActivityIntervalEvent> activityEvents =
|
||||
builder.buildEsperActivityIntervalEvents(session, driver);
|
||||
List<TachographEsperActivityIntervalEvent> drivingEvents =
|
||||
builder.buildEsperDrivingIntervalEvents(session, driver);
|
||||
|
||||
assertThat(activityEvents).hasSize(2);
|
||||
assertThat(activityEvents).extracting(TachographEsperActivityIntervalEvent::activityType)
|
||||
.containsExactly("WORK", "DRIVE");
|
||||
assertThat(activityEvents).extracting(TachographEsperActivityIntervalEvent::driverKey)
|
||||
.containsOnly("12:123");
|
||||
|
||||
assertThat(drivingEvents).hasSize(1);
|
||||
assertThat(drivingEvents.get(0).intervalId()).isEqualTo("ACT-2");
|
||||
assertThat(drivingEvents.get(0).activityType()).isEqualTo("DRIVE");
|
||||
assertThat(drivingEvents.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
|
||||
assertThat(drivingEvents.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:30:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildsEsperVehicleUsageIntervalEventsFromResolvedTimeline() {
|
||||
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-01T11:00:00Z"),
|
||||
100L,
|
||||
200L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"a"
|
||||
),
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-2",
|
||||
OffsetDateTime.parse("2026-05-01T12:00:00Z"),
|
||||
null,
|
||||
201L,
|
||||
null,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"b"
|
||||
)
|
||||
),
|
||||
List.of(),
|
||||
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, 0, 2, 0, 0, 0),
|
||||
List.of(),
|
||||
Instant.now(),
|
||||
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||
);
|
||||
|
||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageEvents =
|
||||
builder.buildEsperVehicleUsageIntervalEvents(session, driver);
|
||||
|
||||
assertThat(vehicleUsageEvents).hasSize(2);
|
||||
assertThat(vehicleUsageEvents).extracting(TachographEsperVehicleUsageIntervalEvent::driverKey)
|
||||
.containsOnly("12:123");
|
||||
assertThat(vehicleUsageEvents).extracting(TachographEsperVehicleUsageIntervalEvent::sessionId)
|
||||
.containsOnly(session.sessionId());
|
||||
assertThat(vehicleUsageEvents.get(0).intervalId()).isEqualTo("CVU-1");
|
||||
assertThat(vehicleUsageEvents.get(1).intervalId()).isEqualTo("CVU-2");
|
||||
assertThat(vehicleUsageEvents.get(1).endedAt()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildsEsperVuCardAbsentIntervalEventsFromVehicleUsageGaps() {
|
||||
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-01T11:00:00Z"),
|
||||
100L,
|
||||
200L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"a"
|
||||
),
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-2",
|
||||
OffsetDateTime.parse("2026-05-01T12:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T13:00:00Z"),
|
||||
201L,
|
||||
260L,
|
||||
"12:REG-2",
|
||||
"VIN-2",
|
||||
"b"
|
||||
),
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-3",
|
||||
OffsetDateTime.parse("2026-05-01T13:00:01Z"),
|
||||
OffsetDateTime.parse("2026-05-01T14:00:00Z"),
|
||||
261L,
|
||||
320L,
|
||||
"12:REG-2",
|
||||
"VIN-2",
|
||||
"c"
|
||||
)
|
||||
),
|
||||
List.of(),
|
||||
List.of(),
|
||||
List.of()
|
||||
);
|
||||
TachographFileSession session = new TachographFileSession(
|
||||
UUID.randomUUID(),
|
||||
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null),
|
||||
Map.of(driver.driverKey(), driver),
|
||||
new ExtractionStats(1, 0, 3, 0, 0, 0),
|
||||
List.of(),
|
||||
Instant.now(),
|
||||
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||
);
|
||||
|
||||
List<TachographEsperVuCardAbsentIntervalEvent> absentIntervals =
|
||||
builder.buildEsperVuCardAbsentIntervalEvents(session, driver);
|
||||
|
||||
assertThat(absentIntervals).hasSize(1);
|
||||
assertThat(absentIntervals.get(0).sessionId()).isEqualTo(session.sessionId());
|
||||
assertThat(absentIntervals.get(0).driverKey()).isEqualTo("12:123");
|
||||
assertThat(absentIntervals.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:01Z"));
|
||||
assertThat(absentIntervals.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T12:00:00Z"));
|
||||
assertThat(absentIntervals.get(0).durationSeconds()).isEqualTo(3599L);
|
||||
assertThat(absentIntervals.get(0).previousUsageIntervalId()).isEqualTo("CVU-1");
|
||||
assertThat(absentIntervals.get(0).nextUsageIntervalId()).isEqualTo("CVU-2");
|
||||
assertThat(absentIntervals.get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||
assertThat(absentIntervals.get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||
assertThat(absentIntervals.get(0).previousVehicleKey()).isEqualTo("VIN-1");
|
||||
assertThat(absentIntervals.get(0).nextVehicleKey()).isEqualTo("VIN-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildsDailyWeeklyRestCandidateIntervalsFromDrivingInterruptionIntervals() {
|
||||
UUID sessionId = UUID.randomUUID();
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals = List.of(
|
||||
new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T20:00:00Z"),
|
||||
36_000L,
|
||||
"ACT-1",
|
||||
"ACT-2",
|
||||
"12:REG-1",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"VIN-1"
|
||||
),
|
||||
new TachographEsperDrivingInterruptionIntervalEvent(
|
||||
sessionId,
|
||||
"12:123",
|
||||
OffsetDateTime.parse("2026-05-02T10:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-02T18:00:00Z"),
|
||||
28_800L,
|
||||
"ACT-3",
|
||||
"ACT-4",
|
||||
"12:REG-1",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"VIN-1"
|
||||
)
|
||||
);
|
||||
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> candidates =
|
||||
builder.buildEsperDailyWeeklyRestCandidateIntervalEvents(drivingInterruptionIntervals, 540);
|
||||
|
||||
assertThat(candidates).hasSize(1);
|
||||
assertThat(candidates.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||
assertThat(candidates.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T20:00:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildsEsperDrivingInterruptionIntervalEventsFromSignificantDrivingGaps() {
|
||||
DriverExtractionSession driver = new DriverExtractionSession(
|
||||
"12:123",
|
||||
null,
|
||||
null,
|
||||
List.of(),
|
||||
List.of(),
|
||||
List.of(),
|
||||
List.of(
|
||||
new ExtractedCardActivityInterval(
|
||||
"ACT-1",
|
||||
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T08:02:00Z"),
|
||||
"DRIVE",
|
||||
"DRIVER",
|
||||
"INSERTED",
|
||||
"SINGLE",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"a"
|
||||
),
|
||||
new ExtractedCardActivityInterval(
|
||||
"ACT-2",
|
||||
OffsetDateTime.parse("2026-05-01T08:02:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T08:10:00Z"),
|
||||
"WORK",
|
||||
"DRIVER",
|
||||
"INSERTED",
|
||||
"SINGLE",
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"b"
|
||||
),
|
||||
new ExtractedCardActivityInterval(
|
||||
"ACT-3",
|
||||
OffsetDateTime.parse("2026-05-01T08:10:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T08:15:00Z"),
|
||||
"DRIVE",
|
||||
"DRIVER",
|
||||
"INSERTED",
|
||||
"SINGLE",
|
||||
"12:REG-2",
|
||||
"VIN-2",
|
||||
"c"
|
||||
)
|
||||
),
|
||||
List.of(),
|
||||
List.of()
|
||||
);
|
||||
TachographFileSession session = new TachographFileSession(
|
||||
UUID.randomUUID(),
|
||||
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null),
|
||||
Map.of(driver.driverKey(), driver),
|
||||
new ExtractionStats(1, 3, 0, 0, 0, 0),
|
||||
List.of(),
|
||||
Instant.now(),
|
||||
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||
);
|
||||
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> interruptions =
|
||||
builder.buildEsperDrivingInterruptionIntervalEvents(session, driver, 1);
|
||||
|
||||
assertThat(interruptions).hasSize(1);
|
||||
assertThat(interruptions.get(0).sessionId()).isEqualTo(session.sessionId());
|
||||
assertThat(interruptions.get(0).driverKey()).isEqualTo("12:123");
|
||||
assertThat(interruptions.get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:02:00Z"));
|
||||
assertThat(interruptions.get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:10:00Z"));
|
||||
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-2",
|
||||
"VIN-1",
|
||||
"VIN-2"
|
||||
)
|
||||
);
|
||||
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<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals =
|
||||
builder.buildEsperDailyWeeklyRestCandidateIntervalEvents(interruptions, 720);
|
||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals =
|
||||
builder.buildEsperDrivingInterruptionVehicleChangeIntervalEvents(dailyWeeklyRestCandidateIntervals);
|
||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals =
|
||||
builder.buildEsperPotentialHomeOvernightStayIntervalEvents(
|
||||
drivingInterruptionVehicleChangeIntervals,
|
||||
vuCardAbsentIntervals
|
||||
);
|
||||
|
||||
assertThat(drivingInterruptionVehicleChangeIntervals).hasSize(1);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package at.procon.eventhub.tachographfilesession.service;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import at.procon.eventhub.config.EventHubProperties;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperEventsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||
|
|
@ -21,6 +23,251 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
class TachographFileSessionProcessingServiceTest {
|
||||
|
||||
@Test
|
||||
void returnsEsperDriverProcessingResultsFromSessionTimeline() {
|
||||
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-01T11:00:00Z"),
|
||||
100L,
|
||||
200L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"vu-1"
|
||||
),
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-2",
|
||||
OffsetDateTime.parse("2026-05-01T12:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T13:00:00Z"),
|
||||
201L,
|
||||
260L,
|
||||
"12:REG-2",
|
||||
"VIN-2",
|
||||
"vu-2"
|
||||
)
|
||||
),
|
||||
List.of(
|
||||
new ExtractedCardActivityInterval("ACT-1", OffsetDateTime.parse("2026-05-01T08:30:00Z"), OffsetDateTime.parse("2026-05-01T09:00:00Z"), "WORK", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "a"),
|
||||
new ExtractedCardActivityInterval("ACT-2", OffsetDateTime.parse("2026-05-01T09:00:00Z"), OffsetDateTime.parse("2026-05-01T10:00: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", 3, "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());
|
||||
|
||||
assertThat(result.sourceKind()).isEqualTo("DRIVER_CARD");
|
||||
assertThat(result.activityIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.drivingIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).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"));
|
||||
assertThat(result.vuCardAbsentIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T12:00:00Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void appliesOccurredWindowToEsperDriverProcessingResults() {
|
||||
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-01T11:00:00Z"),
|
||||
100L,
|
||||
200L,
|
||||
"12:REG-1",
|
||||
"VIN-1",
|
||||
"vu-1"
|
||||
),
|
||||
new ExtractedCardVehicleUsageInterval(
|
||||
"CVU-2",
|
||||
OffsetDateTime.parse("2026-05-01T12:00:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T13:00:00Z"),
|
||||
201L,
|
||||
260L,
|
||||
"12:REG-2",
|
||||
"VIN-2",
|
||||
"vu-2"
|
||||
)
|
||||
),
|
||||
List.of(
|
||||
new ExtractedCardActivityInterval("ACT-1", OffsetDateTime.parse("2026-05-01T08:30:00Z"), OffsetDateTime.parse("2026-05-01T09:00:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "a"),
|
||||
new ExtractedCardActivityInterval("ACT-2", OffsetDateTime.parse("2026-05-01T09:00:00Z"), OffsetDateTime.parse("2026-05-01T10:00:00Z"), "WORK", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "b"),
|
||||
new ExtractedCardActivityInterval("ACT-3", OffsetDateTime.parse("2026-05-01T10:00:00Z"), OffsetDateTime.parse("2026-05-01T10:05:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-2", "VIN-2", "c")
|
||||
),
|
||||
List.of(),
|
||||
List.of()
|
||||
);
|
||||
TachographFileSession session = new TachographFileSession(
|
||||
UUID.randomUUID(),
|
||||
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "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-01T08:45:00Z"),
|
||||
OffsetDateTime.parse("2026-05-01T12:30:00Z"),
|
||||
3,
|
||||
720
|
||||
)
|
||||
);
|
||||
|
||||
assertThat(result.requestedFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:45:00Z"));
|
||||
assertThat(result.requestedTo()).isEqualTo(OffsetDateTime.parse("2026-05-01T12:30:00Z"));
|
||||
assertThat(result.activityIntervalCount()).isEqualTo(3);
|
||||
assertThat(result.activityIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:45:00Z"));
|
||||
assertThat(result.activityIntervals().get(0).clippedToRequestedPeriod()).isTrue();
|
||||
assertThat(result.drivingIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
||||
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(0);
|
||||
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.vehicleUsageIntervalCount()).isEqualTo(2);
|
||||
assertThat(result.vehicleUsageIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:45:00Z"));
|
||||
assertThat(result.vehicleUsageIntervals().get(0).odometerBeginKm()).isNull();
|
||||
assertThat(result.vehicleUsageIntervals().get(1).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T12:30:00Z"));
|
||||
assertThat(result.vehicleUsageIntervals().get(1).odometerEndKm()).isNull();
|
||||
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-2", "VIN-2", "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.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(1);
|
||||
assertThat(result.dailyWeeklyRestCandidateIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||
assertThat(result.dailyWeeklyRestCandidateIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T23:00:00Z"));
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||
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