Add tachograph Esper driver event projections
This commit is contained in:
parent
9e6f8efb26
commit
0e2b83270c
|
|
@ -351,6 +351,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"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 operating periods",
|
"name": "Process tachograph file session operating periods",
|
||||||
"request": {
|
"request": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package at.procon.eventhub.tachographfilesession.api;
|
package at.procon.eventhub.tachographfilesession.api;
|
||||||
|
|
||||||
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||||
|
|
@ -66,6 +67,14 @@ public class TachographFileSessionController {
|
||||||
return ResponseEntity.ok(service.getDriver(sessionId, driverKey));
|
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/operating-periods")
|
@PostMapping("/{sessionId}/drivers/{driverKey}/processing/operating-periods")
|
||||||
public ResponseEntity<TachographOperatingPeriodsProcessingResultDto> evaluateOperatingPeriods(
|
public ResponseEntity<TachographOperatingPeriodsProcessingResultDto> evaluateOperatingPeriods(
|
||||||
@PathVariable UUID sessionId,
|
@PathVariable UUID sessionId,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.dto;
|
||||||
|
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
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,
|
||||||
|
int activityIntervalCount,
|
||||||
|
int drivingIntervalCount,
|
||||||
|
int vehicleUsageIntervalCount,
|
||||||
|
int vuCardAbsentIntervalCount,
|
||||||
|
List<TachographEsperActivityIntervalEvent> activityIntervals,
|
||||||
|
List<TachographEsperActivityIntervalEvent> drivingIntervals,
|
||||||
|
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
||||||
|
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||||
|
List<String> notes
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,11 @@ package at.procon.eventhub.tachographfilesession.model;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public record ResolvedVehicleUsageInterval(
|
public record ResolvedVehicleUsageInterval(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
String intervalId,
|
String intervalId,
|
||||||
OffsetDateTime from,
|
OffsetDateTime from,
|
||||||
OffsetDateTime to,
|
OffsetDateTime to,
|
||||||
|
|
@ -17,6 +20,8 @@ public record ResolvedVehicleUsageInterval(
|
||||||
List<String> sourceIntervalIds
|
List<String> sourceIntervalIds
|
||||||
) {
|
) {
|
||||||
public static ResolvedVehicleUsageInterval resolved(
|
public static ResolvedVehicleUsageInterval resolved(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
String intervalId,
|
String intervalId,
|
||||||
OffsetDateTime from,
|
OffsetDateTime from,
|
||||||
OffsetDateTime to,
|
OffsetDateTime to,
|
||||||
|
|
@ -28,6 +33,8 @@ public record ResolvedVehicleUsageInterval(
|
||||||
List<String> sourceIntervalIds
|
List<String> sourceIntervalIds
|
||||||
) {
|
) {
|
||||||
return new ResolvedVehicleUsageInterval(
|
return new ResolvedVehicleUsageInterval(
|
||||||
|
sessionId,
|
||||||
|
driverKey,
|
||||||
intervalId,
|
intervalId,
|
||||||
from,
|
from,
|
||||||
to,
|
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,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;
|
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.DriverExtractionSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval;
|
import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
|
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
|
||||||
|
|
@ -8,22 +18,48 @@ import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
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.TachographFileSession;
|
||||||
import java.time.Duration;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
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.stereotype.Component;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class DriverTimelineBuilder {
|
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 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) {
|
public ResolvedDriverTimeline build(TachographFileSession session, DriverExtractionSession driverSession) {
|
||||||
String sourceKind = session.metadata().driverCardFile() ? "DRIVER_CARD" : "VEHICLE_UNIT";
|
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<ResolvedActivityInterval> activityIntervals = resolveActivities(driverSession.cardActivityIntervals(), sourceKind);
|
||||||
List<ExtractedSupportEvent> supportEvents = driverSession.supportEvents().stream()
|
List<ExtractedSupportEvent> supportEvents = driverSession.supportEvents().stream()
|
||||||
.sorted(Comparator.comparing(ExtractedSupportEvent::occurredAt)
|
.sorted(Comparator.comparing(ExtractedSupportEvent::occurredAt)
|
||||||
|
|
@ -44,7 +80,200 @@ 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<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<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(
|
private List<ResolvedVehicleUsageInterval> mergeVehicleUsageIntervals(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
List<ExtractedCardVehicleUsageInterval> rawIntervals,
|
List<ExtractedCardVehicleUsageInterval> rawIntervals,
|
||||||
String sourceKind
|
String sourceKind
|
||||||
) {
|
) {
|
||||||
|
|
@ -54,6 +283,8 @@ public class DriverTimelineBuilder {
|
||||||
List<ResolvedVehicleUsageInterval> sorted = rawIntervals.stream()
|
List<ResolvedVehicleUsageInterval> sorted = rawIntervals.stream()
|
||||||
.filter(interval -> interval.from() != null && (interval.to() == null || interval.to().isAfter(interval.from())))
|
.filter(interval -> interval.from() != null && (interval.to() == null || interval.to().isAfter(interval.from())))
|
||||||
.map(interval -> ResolvedVehicleUsageInterval.resolved(
|
.map(interval -> ResolvedVehicleUsageInterval.resolved(
|
||||||
|
sessionId,
|
||||||
|
driverKey,
|
||||||
interval.intervalId(),
|
interval.intervalId(),
|
||||||
interval.from(),
|
interval.from(),
|
||||||
interval.to(),
|
interval.to(),
|
||||||
|
|
@ -79,6 +310,8 @@ public class DriverTimelineBuilder {
|
||||||
if (canMerge(current, next)) {
|
if (canMerge(current, next)) {
|
||||||
currentSources.addAll(next.sourceIntervalIds());
|
currentSources.addAll(next.sourceIntervalIds());
|
||||||
current = ResolvedVehicleUsageInterval.resolved(
|
current = ResolvedVehicleUsageInterval.resolved(
|
||||||
|
current.sessionId(),
|
||||||
|
current.driverKey(),
|
||||||
current.intervalId() + "+" + next.intervalId(),
|
current.intervalId() + "+" + next.intervalId(),
|
||||||
current.from(),
|
current.from(),
|
||||||
mergedTo(current.to(), next.to()),
|
mergedTo(current.to(), next.to()),
|
||||||
|
|
@ -220,4 +453,210 @@ public class DriverTimelineBuilder {
|
||||||
private OffsetDateTime mergeBoundary(OffsetDateTime endInclusive) {
|
private OffsetDateTime mergeBoundary(OffsetDateTime endInclusive) {
|
||||||
return endInclusive == null ? OffsetDateTime.MAX : endInclusive.plusSeconds(1);
|
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", 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("startedAt", OffsetDateTime.class);
|
||||||
|
definition.put("endedAt", OffsetDateTime.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("startedAt", OffsetDateTime.class);
|
||||||
|
definition.put("endedAt", OffsetDateTime.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> 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("startedAt", interval.from());
|
||||||
|
event.put("endedAt", interval.to());
|
||||||
|
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 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("startedAt", interval.from());
|
||||||
|
event.put("endedAt", interval.to());
|
||||||
|
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 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 collectVuCardAbsentIntervalEvents(
|
||||||
|
EventBean[] newData,
|
||||||
|
List<TachographEsperVuCardAbsentIntervalEvent> target
|
||||||
|
) {
|
||||||
|
if (newData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (EventBean event : newData) {
|
||||||
|
target.add(new TachographEsperVuCardAbsentIntervalEvent(
|
||||||
|
(UUID) event.get("sessionId"),
|
||||||
|
(String) event.get("driverKey"),
|
||||||
|
(OffsetDateTime) event.get("startedAt"),
|
||||||
|
(OffsetDateTime) event.get("endedAt"),
|
||||||
|
(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")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package at.procon.eventhub.tachographfilesession.service;
|
package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
import at.procon.eventhub.config.EventHubProperties;
|
import at.procon.eventhub.config.EventHubProperties;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||||
|
|
@ -10,7 +11,10 @@ import at.procon.eventhub.tachographfilesession.model.ProcessedOperatingPeriod;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ProcessedShiftDrivingEvaluation;
|
import at.procon.eventhub.tachographfilesession.model.ProcessedShiftDrivingEvaluation;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -113,6 +117,45 @@ public class TachographFileSessionProcessingService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TachographEsperDriverProcessingResultDto getEsperDriverProcessingResults(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey
|
||||||
|
) {
|
||||||
|
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);
|
||||||
|
List<TachographEsperActivityIntervalEvent> activityIntervals =
|
||||||
|
driverTimelineBuilder.buildEsperActivityIntervalEvents(sessionId, driverKey, timeline);
|
||||||
|
List<TachographEsperActivityIntervalEvent> drivingIntervals =
|
||||||
|
driverTimelineBuilder.buildEsperDrivingIntervalEvents(sessionId, driverKey, timeline);
|
||||||
|
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals =
|
||||||
|
driverTimelineBuilder.buildEsperVehicleUsageIntervalEvents(timeline);
|
||||||
|
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals =
|
||||||
|
driverTimelineBuilder.buildEsperVuCardAbsentIntervalEvents(timeline);
|
||||||
|
|
||||||
|
return new TachographEsperDriverProcessingResultDto(
|
||||||
|
sessionId,
|
||||||
|
driverKey,
|
||||||
|
timeline.sourceKind(),
|
||||||
|
timeline.loadedFrom(),
|
||||||
|
timeline.loadedTo(),
|
||||||
|
activityIntervals.size(),
|
||||||
|
drivingIntervals.size(),
|
||||||
|
vehicleUsageIntervals.size(),
|
||||||
|
vuCardAbsentIntervals.size(),
|
||||||
|
activityIntervals,
|
||||||
|
drivingIntervals,
|
||||||
|
vehicleUsageIntervals,
|
||||||
|
vuCardAbsentIntervals,
|
||||||
|
esperProjectionNotes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private List<ResolvedActivityInterval> synthesizeUnknownGaps(
|
private List<ResolvedActivityInterval> synthesizeUnknownGaps(
|
||||||
List<ResolvedActivityInterval> knownIntervals,
|
List<ResolvedActivityInterval> knownIntervals,
|
||||||
Duration gapDetectionTolerance
|
Duration gapDetectionTolerance
|
||||||
|
|
@ -660,6 +703,14 @@ 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.",
|
||||||
|
"VU card-absent intervals are gaps between consecutive normalized vehicle-usage intervals for the same driver."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private OffsetDateTime utc(OffsetDateTime value) {
|
private OffsetDateTime utc(OffsetDateTime value) {
|
||||||
return value == null ? null : value.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
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,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,15 @@
|
||||||
|
@name('vehicleUsageIntervals')
|
||||||
|
select
|
||||||
|
sessionId,
|
||||||
|
driverKey,
|
||||||
|
intervalId,
|
||||||
|
startedAt,
|
||||||
|
endedAt,
|
||||||
|
durationSeconds,
|
||||||
|
odometerBeginKm,
|
||||||
|
odometerEndKm,
|
||||||
|
registrationKey,
|
||||||
|
vehicleKey,
|
||||||
|
sourceKind,
|
||||||
|
sourceIntervalIds
|
||||||
|
from TachographVehicleUsageIntervalInputEvent
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
create context PerDriver partition by driverKey from TachographVehicleUsageIntervalInputEvent;
|
||||||
|
|
||||||
|
create schema VuCardAbsentInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAt java.time.OffsetDateTime,
|
||||||
|
endedAt java.time.OffsetDateTime,
|
||||||
|
durationSeconds long,
|
||||||
|
previousUsageIntervalId string,
|
||||||
|
nextUsageIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
|
context PerDriver
|
||||||
|
create window PreviousVehicleUsageInterval#lastevent as TachographVehicleUsageIntervalInputEvent;
|
||||||
|
|
||||||
|
context PerDriver
|
||||||
|
@Priority(30)
|
||||||
|
on TachographVehicleUsageIntervalInputEvent as next
|
||||||
|
insert into VuCardAbsentInterval
|
||||||
|
select
|
||||||
|
prev.sessionId as sessionId,
|
||||||
|
prev.driverKey as driverKey,
|
||||||
|
prev.endedAt.plusSeconds(1) as startedAt,
|
||||||
|
next.startedAt as endedAt,
|
||||||
|
java.time.Duration.between(prev.endedAt.plusSeconds(1), next.startedAt).getSeconds() as durationSeconds,
|
||||||
|
prev.intervalId as previousUsageIntervalId,
|
||||||
|
next.intervalId as nextUsageIntervalId,
|
||||||
|
prev.registrationKey as previousRegistrationKey,
|
||||||
|
next.registrationKey as nextRegistrationKey,
|
||||||
|
prev.vehicleKey as previousVehicleKey,
|
||||||
|
next.vehicleKey as nextVehicleKey
|
||||||
|
from PreviousVehicleUsageInterval as prev
|
||||||
|
where prev.endedAt is not null
|
||||||
|
and next.startedAt is not null
|
||||||
|
and next.startedAt.isAfter(prev.endedAt.plusSeconds(1));
|
||||||
|
|
||||||
|
context PerDriver
|
||||||
|
@Priority(20)
|
||||||
|
on TachographVehicleUsageIntervalInputEvent
|
||||||
|
delete from PreviousVehicleUsageInterval;
|
||||||
|
|
||||||
|
context PerDriver
|
||||||
|
@Priority(10)
|
||||||
|
on TachographVehicleUsageIntervalInputEvent as current
|
||||||
|
insert into PreviousVehicleUsageInterval
|
||||||
|
select *;
|
||||||
|
|
||||||
|
@name('vuCardAbsentIntervals')
|
||||||
|
context PerDriver
|
||||||
|
select * from VuCardAbsentInterval;
|
||||||
|
|
@ -9,6 +9,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||||
|
|
@ -17,8 +18,12 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteR
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
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.TachographFileSessionProcessingService;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -58,6 +63,84 @@ class TachographFileSessionControllerTest {
|
||||||
when(service.getSession(sessionId)).thenReturn(summary);
|
when(service.getSession(sessionId)).thenReturn(summary);
|
||||||
when(service.listDrivers(sessionId)).thenReturn(new TachographFileSessionListDriversResponse(sessionId, List.of(driver)));
|
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(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(sessionId, "12:123"))
|
||||||
|
.thenReturn(new TachographEsperDriverProcessingResultDto(
|
||||||
|
sessionId,
|
||||||
|
"12:123",
|
||||||
|
"DRIVER_CARD",
|
||||||
|
OffsetDateTime.parse("2026-05-12T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-12T12:00:00Z"),
|
||||||
|
2,
|
||||||
|
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 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)))
|
when(processingService.evaluateOperatingPeriods(eq(sessionId), eq("12:123"), org.mockito.ArgumentMatchers.any(TachographOperatingPeriodsProcessingRequest.class)))
|
||||||
.thenReturn(new TachographOperatingPeriodsProcessingResultDto(
|
.thenReturn(new TachographOperatingPeriodsProcessingResultDto(
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|
@ -104,6 +187,14 @@ class TachographFileSessionControllerTest {
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.driverKey").value("12:123"));
|
.andExpect(jsonPath("$.driverKey").value("12:123"));
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers/{driverKey}/processing/esper-events", sessionId, "12:123"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.driverKey").value("12:123"))
|
||||||
|
.andExpect(jsonPath("$.sourceKind").value("DRIVER_CARD"))
|
||||||
|
.andExpect(jsonPath("$.activityIntervalCount").value(2))
|
||||||
|
.andExpect(jsonPath("$.vuCardAbsentIntervalCount").value(1))
|
||||||
|
.andExpect(jsonPath("$.drivingIntervals[0].activityType").value("DRIVE"));
|
||||||
|
|
||||||
mockMvc.perform(post("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers/{driverKey}/processing/operating-periods", sessionId, "12:123")
|
mockMvc.perform(post("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers/{driverKey}/processing/operating-periods", sessionId, "12:123")
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.content("""
|
.content("""
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
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.TachographFileSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
@ -100,6 +103,8 @@ class DriverTimelineBuilderTest {
|
||||||
|
|
||||||
assertThat(timeline.sourceKind()).isEqualTo("DRIVER_CARD");
|
assertThat(timeline.sourceKind()).isEqualTo("DRIVER_CARD");
|
||||||
assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
|
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).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.vehicleUsageIntervals().get(0).to()).isEqualTo(OffsetDateTime.parse("2026-05-02T08:00:00Z"));
|
||||||
assertThat(timeline.activityIntervals()).hasSize(1);
|
assertThat(timeline.activityIntervals()).hasSize(1);
|
||||||
|
|
@ -169,9 +174,205 @@ class DriverTimelineBuilderTest {
|
||||||
ResolvedDriverTimeline timeline = builder.build(session, driver);
|
ResolvedDriverTimeline timeline = builder.build(session, driver);
|
||||||
|
|
||||||
assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
|
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).from()).isEqualTo(OffsetDateTime.parse("2026-05-01T00:00:00Z"));
|
||||||
assertThat(timeline.vehicleUsageIntervals().get(0).to()).isNull();
|
assertThat(timeline.vehicleUsageIntervals().get(0).to()).isNull();
|
||||||
assertThat(timeline.vehicleUsageIntervals().get(0).sourceIntervalIds()).containsExactly("CVU-1", "CVU-2");
|
assertThat(timeline.vehicleUsageIntervals().get(0).sourceIntervalIds()).containsExactly("CVU-1", "CVU-2");
|
||||||
assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-02T09:00:00Z"));
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package at.procon.eventhub.tachographfilesession.service;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import at.procon.eventhub.config.EventHubProperties;
|
import at.procon.eventhub.config.EventHubProperties;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingRequest;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographOperatingPeriodsProcessingResultDto;
|
||||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||||
|
|
@ -21,6 +22,74 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class TachographFileSessionProcessingServiceTest {
|
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.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
|
@Test
|
||||||
void evaluatesOperatingPeriodsFromSessionTimeline() {
|
void evaluatesOperatingPeriodsFromSessionTimeline() {
|
||||||
EventHubProperties properties = new EventHubProperties();
|
EventHubProperties properties = new EventHubProperties();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue