Decouple runtime timelines from tachograph models

This commit is contained in:
trifonovt 2026-06-05 14:39:36 +02:00
parent aa360c9f02
commit 5cf0cedc41
30 changed files with 746 additions and 190 deletions

View File

@ -17,12 +17,12 @@ import at.procon.eventhub.processing.eventprocessing.validation.RuntimeMixedSour
import at.procon.eventhub.processing.eventprocessing.validation.RuntimeTachographParityValidationApiRequest; import at.procon.eventhub.processing.eventprocessing.validation.RuntimeTachographParityValidationApiRequest;
import at.procon.eventhub.processing.eventprocessing.validation.RuntimeTachographParityValidationResultDto; import at.procon.eventhub.processing.eventprocessing.validation.RuntimeTachographParityValidationResultDto;
import at.procon.eventhub.processing.eventprocessing.validation.RuntimeTachographParityValidationService; import at.procon.eventhub.processing.eventprocessing.validation.RuntimeTachographParityValidationService;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.service.UnifiedRuntimeDerivedProjectionService; import at.procon.eventhub.processing.service.UnifiedRuntimeDerivedProjectionService;
import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService; import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService;
import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService; import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService;
import at.procon.eventhub.processing.service.RuntimeDriverWorkingTimeScopeProcessingService; import at.procon.eventhub.processing.service.RuntimeDriverWorkingTimeScopeProcessingService;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -106,7 +106,7 @@ public class UnifiedRuntimeProcessingController {
} }
@PostMapping("/driver-timeline") @PostMapping("/driver-timeline")
public ResponseEntity<ResolvedDriverTimeline> loadDriverTimeline( public ResponseEntity<RuntimeDriverTimeline> loadDriverTimeline(
@RequestBody UnifiedRuntimeProcessingApiRequest request @RequestBody UnifiedRuntimeProcessingApiRequest request
) { ) {
return ResponseEntity.ok(runtimeDriverTimelineService.loadDriverTimeline(request.toRuntimeRequest())); return ResponseEntity.ok(runtimeDriverTimelineService.loadDriverTimeline(request.toRuntimeRequest()));

View File

@ -1,7 +1,7 @@
package at.procon.eventhub.processing.dto; package at.procon.eventhub.processing.dto;
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval; import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval; import at.procon.eventhub.processing.model.RuntimeVehicleUsageInterval;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
public record RuntimeVehicleUsageIntervalDebugDto( public record RuntimeVehicleUsageIntervalDebugDto(
@ -12,7 +12,7 @@ public record RuntimeVehicleUsageIntervalDebugDto(
String vehicleKey, String vehicleKey,
String sourceKind String sourceKind
) { ) {
public static RuntimeVehicleUsageIntervalDebugDto from(ResolvedVehicleUsageInterval interval) { public static RuntimeVehicleUsageIntervalDebugDto from(RuntimeVehicleUsageInterval interval) {
if (interval == null) { if (interval == null) {
return null; return null;
} }

View File

@ -14,7 +14,7 @@ import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest; import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.service.RuntimeDriverVehicleEvidenceAttachmentService; import at.procon.eventhub.processing.service.RuntimeDriverVehicleEvidenceAttachmentService;
import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
@ -260,7 +260,7 @@ public class VehicleEvidenceAttachmentModule implements RuntimeProcessingModule
} }
private String driverKey(EventHubEventDto event) { private String driverKey(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.driverKey(event); return RuntimeEntityReferenceResolver.driverKey(event);
} }
private JsonNode rawPayload(EventHubEventDto event) { private JsonNode rawPayload(EventHubEventDto event) {

View File

@ -9,7 +9,7 @@ import at.procon.eventhub.processing.eventprocessing.module.RuntimeProcessingMod
import at.procon.eventhub.processing.eventprocessing.module.RuntimeProcessingModuleResult; import at.procon.eventhub.processing.eventprocessing.module.RuntimeProcessingModuleResult;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@ -156,7 +156,7 @@ public final class DriverWorkingTimeEplEventMapper {
JsonNode attributes = attributes(sourceEvent); JsonNode attributes = attributes(sourceEvent);
String intervalId = RuntimeEventIdentityResolver.presentationIntervalId(sourceEvent); String intervalId = RuntimeEventIdentityResolver.presentationIntervalId(sourceEvent);
String runtimeIntervalKey = RuntimeEventIdentityResolver.runtimeIntervalKey(sourceEvent); String runtimeIntervalKey = RuntimeEventIdentityResolver.runtimeIntervalKey(sourceEvent);
String driverKey = TachographRuntimeIdentityResolver.driverKey(sourceEvent); String driverKey = RuntimeEntityReferenceResolver.driverKey(sourceEvent);
if (driverKey == null || intervalId == null || runtimeIntervalKey == null) { if (driverKey == null || intervalId == null || runtimeIntervalKey == null) {
return null; return null;
} }
@ -175,8 +175,8 @@ public final class DriverWorkingTimeEplEventMapper {
event.put("cardSlot", firstNonBlank(text(raw, "cardSlot"), text(attributes, "cardSlot"))); event.put("cardSlot", firstNonBlank(text(raw, "cardSlot"), text(attributes, "cardSlot")));
event.put("cardStatus", firstNonBlank(text(raw, "cardStatus"), text(attributes, "cardStatus"))); event.put("cardStatus", firstNonBlank(text(raw, "cardStatus"), text(attributes, "cardStatus")));
event.put("drivingStatus", firstNonBlank(text(raw, "drivingStatus"), text(attributes, "drivingStatus"))); event.put("drivingStatus", firstNonBlank(text(raw, "drivingStatus"), text(attributes, "drivingStatus")));
event.put("registrationKey", TachographRuntimeIdentityResolver.registrationKey(sourceEvent)); event.put("registrationKey", RuntimeEntityReferenceResolver.registrationKey(sourceEvent));
event.put("vehicleKey", TachographRuntimeIdentityResolver.vehicleKey(sourceEvent)); event.put("vehicleKey", RuntimeEntityReferenceResolver.vehicleKey(sourceEvent));
event.put("sourceKind", firstNonBlank(text(raw, "sourceKind"), sourceKind(sourceEvent))); event.put("sourceKind", firstNonBlank(text(raw, "sourceKind"), sourceKind(sourceEvent)));
event.put("synthetic", booleanValue(raw, "synthetic", false)); event.put("synthetic", booleanValue(raw, "synthetic", false));
event.put("clippedToRequestedPeriod", booleanValue(raw, "clippedToRequestedPeriod", false)); event.put("clippedToRequestedPeriod", booleanValue(raw, "clippedToRequestedPeriod", false));
@ -199,7 +199,7 @@ public final class DriverWorkingTimeEplEventMapper {
JsonNode raw = rawPayload(sourceEvent); JsonNode raw = rawPayload(sourceEvent);
String intervalId = RuntimeEventIdentityResolver.presentationIntervalId(sourceEvent); String intervalId = RuntimeEventIdentityResolver.presentationIntervalId(sourceEvent);
String runtimeIntervalKey = RuntimeEventIdentityResolver.runtimeIntervalKey(sourceEvent); String runtimeIntervalKey = RuntimeEventIdentityResolver.runtimeIntervalKey(sourceEvent);
String driverKey = TachographRuntimeIdentityResolver.driverKey(sourceEvent); String driverKey = RuntimeEntityReferenceResolver.driverKey(sourceEvent);
if (driverKey == null || intervalId == null || runtimeIntervalKey == null) { if (driverKey == null || intervalId == null || runtimeIntervalKey == null) {
return null; return null;
} }
@ -214,8 +214,8 @@ public final class DriverWorkingTimeEplEventMapper {
event.put("lifecycle", sourceEvent.lifecycle().name()); event.put("lifecycle", sourceEvent.lifecycle().name());
event.put("occurredAt", sourceEvent.occurredAt()); event.put("occurredAt", sourceEvent.occurredAt());
event.put("occurredAtEpochSecond", sourceEvent.occurredAt().toEpochSecond()); event.put("occurredAtEpochSecond", sourceEvent.occurredAt().toEpochSecond());
event.put("registrationKey", TachographRuntimeIdentityResolver.registrationKey(sourceEvent)); event.put("registrationKey", RuntimeEntityReferenceResolver.registrationKey(sourceEvent));
event.put("vehicleKey", TachographRuntimeIdentityResolver.vehicleKey(sourceEvent)); event.put("vehicleKey", RuntimeEntityReferenceResolver.vehicleKey(sourceEvent));
event.put("sourceKind", firstNonBlank(text(raw, "sourceKind"), sourceKind(sourceEvent))); event.put("sourceKind", firstNonBlank(text(raw, "sourceKind"), sourceKind(sourceEvent)));
event.put("odometerKm", odometerKm(sourceEvent, raw)); event.put("odometerKm", odometerKm(sourceEvent, raw));
return event; return event;

View File

@ -8,7 +8,7 @@ import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType; import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.dto.GeoPointDto; import at.procon.eventhub.dto.GeoPointDto;
import at.procon.eventhub.dto.VehicleRefDto; import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
@ -82,9 +82,9 @@ public class RuntimeSupportEvidenceNormalizer {
event.eventDomain() == null ? null : event.eventDomain().name(), event.eventDomain() == null ? null : event.eventDomain().name(),
event.eventType() == null ? null : event.eventType().name(), event.eventType() == null ? null : event.eventType().name(),
event.lifecycle() == null ? null : event.lifecycle().name(), event.lifecycle() == null ? null : event.lifecycle().name(),
firstNonBlank(TachographRuntimeIdentityResolver.driverKey(event), fallbackDriverKey), firstNonBlank(RuntimeEntityReferenceResolver.driverKey(event), fallbackDriverKey),
TachographRuntimeIdentityResolver.vehicleKey(event), RuntimeEntityReferenceResolver.vehicleKey(event),
TachographRuntimeIdentityResolver.registrationKey(event), RuntimeEntityReferenceResolver.registrationKey(event),
event.occurredAt(), event.occurredAt(),
event.occurredAt() == null ? null : event.occurredAt().toEpochSecond(), event.occurredAt() == null ? null : event.occurredAt().toEpochSecond(),
latitude, latitude,
@ -288,7 +288,7 @@ public class RuntimeSupportEvidenceNormalizer {
} }
private JsonNode rawPayload(EventHubEventDto event) { private JsonNode rawPayload(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.rawPayload(event); return RuntimeEntityReferenceResolver.rawPayload(event);
} }
private String detailText(EventHubEventDto event, String field) { private String detailText(EventHubEventDto event, String field) {

View File

@ -0,0 +1,99 @@
package at.procon.eventhub.processing.model;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.List;
public record RuntimeActivityInterval(
String intervalId,
OffsetDateTime from,
OffsetDateTime to,
long durationSeconds,
String activityType,
String slot,
String cardStatus,
String drivingStatus,
String registrationKey,
String vehicleKey,
String sourceKind,
List<String> sourceIntervalIds,
boolean synthetic,
boolean clippedToRequestedPeriod,
String level
) {
public static RuntimeActivityInterval raw(
String intervalId,
OffsetDateTime from,
OffsetDateTime to,
String activityType,
String slot,
String cardStatus,
String drivingStatus,
String registrationKey,
String vehicleKey,
String sourceKind,
List<String> sourceIntervalIds
) {
return new RuntimeActivityInterval(
intervalId,
from,
to,
Duration.between(from, to).getSeconds(),
activityType,
slot,
cardStatus,
drivingStatus,
registrationKey,
vehicleKey,
sourceKind,
sourceIntervalIds == null ? List.of() : List.copyOf(sourceIntervalIds),
false,
false,
"RAW_INTERVAL"
);
}
public RuntimeActivityInterval withTime(OffsetDateTime newFrom, OffsetDateTime newTo, boolean clipped) {
return new RuntimeActivityInterval(
intervalId,
newFrom,
newTo,
Duration.between(newFrom, newTo).getSeconds(),
activityType,
slot,
cardStatus,
drivingStatus,
registrationKey,
vehicleKey,
sourceKind,
sourceIntervalIds,
synthetic,
clipped,
level
);
}
public RuntimeActivityInterval asMerged(
String mergedIntervalId,
OffsetDateTime newTo,
List<String> mergedSourceIntervalIds
) {
return new RuntimeActivityInterval(
mergedIntervalId,
from,
newTo,
Duration.between(from, newTo).getSeconds(),
activityType,
slot,
cardStatus,
drivingStatus,
registrationKey,
vehicleKey,
sourceKind,
mergedSourceIntervalIds == null ? List.of() : List.copyOf(mergedSourceIntervalIds),
synthetic,
clippedToRequestedPeriod,
"MERGED_ACTIVITY"
);
}
}

View File

@ -0,0 +1,15 @@
package at.procon.eventhub.processing.model;
import java.time.OffsetDateTime;
import java.util.List;
public record RuntimeDriverTimeline(
String sourceKind,
OffsetDateTime loadedFrom,
OffsetDateTime loadedTo,
List<RuntimeVehicleUsageInterval> vehicleUsageIntervals,
List<RuntimeActivityInterval> activityIntervals,
List<RuntimeSupportEvent> supportEvents,
List<RuntimeExtractionWarning> warnings
) {
}

View File

@ -0,0 +1,8 @@
package at.procon.eventhub.processing.model;
public record RuntimeExtractionWarning(
String code,
String message,
String rawRecordPath
) {
}

View File

@ -0,0 +1,30 @@
package at.procon.eventhub.processing.model;
import java.math.BigDecimal;
import java.time.OffsetDateTime;
public record RuntimeSupportEvent(
String eventId,
String driverKey,
OffsetDateTime occurredAt,
String eventDomain,
String eventType,
String eventLifecycle,
String slot,
String registrationKey,
String vehicleKey,
String country,
String region,
String countryFrom,
String countryTo,
String operation,
BigDecimal latitude,
BigDecimal longitude,
String authenticationStatus,
Long odometerKm,
String code,
BigDecimal avgSpeedKmh,
BigDecimal maxSpeedKmh,
String rawRecordPath
) {
}

View File

@ -0,0 +1,50 @@
package at.procon.eventhub.processing.model;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
public record RuntimeVehicleUsageInterval(
UUID sessionId,
String driverKey,
String intervalId,
OffsetDateTime from,
OffsetDateTime to,
long durationSeconds,
Long odometerBeginKm,
Long odometerEndKm,
String registrationKey,
String vehicleKey,
String sourceKind,
List<String> sourceIntervalIds
) {
public static RuntimeVehicleUsageInterval resolved(
UUID sessionId,
String driverKey,
String intervalId,
OffsetDateTime from,
OffsetDateTime to,
Long odometerBeginKm,
Long odometerEndKm,
String registrationKey,
String vehicleKey,
String sourceKind,
List<String> sourceIntervalIds
) {
return new RuntimeVehicleUsageInterval(
sessionId,
driverKey,
intervalId,
from,
to,
to == null ? 0L : Duration.between(from, to).getSeconds(),
odometerBeginKm,
odometerEndKm,
registrationKey,
vehicleKey,
sourceKind,
sourceIntervalIds == null ? List.of() : List.copyOf(sourceIntervalIds)
);
}
}

View File

@ -7,12 +7,12 @@ import at.procon.eventhub.processing.dto.RuntimeVehicleEvidenceAttachmentDecisio
import at.procon.eventhub.processing.dto.RuntimeVehicleUsageIntervalDebugDto; import at.procon.eventhub.processing.dto.RuntimeVehicleUsageIntervalDebugDto;
import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeClassifier; import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeClassifier;
import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeType; import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeType;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeDriverVehicleEvidenceAttachmentResult; import at.procon.eventhub.processing.model.RuntimeDriverVehicleEvidenceAttachmentResult;
import at.procon.eventhub.processing.model.RuntimeVehicleUsageInterval;
import at.procon.eventhub.processing.support.RuntimeDriverWorkingTimeAdapter;
import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
import at.procon.eventhub.tachographfilesession.service.TachographDriverWorkingTimeAdapter;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@ -63,14 +63,14 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
int vehicleEvidencePaddingMinutes, int vehicleEvidencePaddingMinutes,
boolean includeDebug boolean includeDebug
) { ) {
ResolvedDriverTimeline timeline = timelineReconstructor.reconstruct( RuntimeDriverTimeline timeline = timelineReconstructor.reconstruct(
null, null,
driverKey, driverKey,
directDriverEvents == null ? List.of() : directDriverEvents directDriverEvents == null ? List.of() : directDriverEvents
); );
List<DriverWorkingTimeVehicleUsageInterval> vehicleUsageIntervals = mergeVehicleUsageIntervals(timeline.vehicleUsageIntervals()) List<DriverWorkingTimeVehicleUsageInterval> vehicleUsageIntervals = mergeVehicleUsageIntervals(timeline.vehicleUsageIntervals())
.stream() .stream()
.map(TachographDriverWorkingTimeAdapter::toVehicleUsageInterval) .map(RuntimeDriverWorkingTimeAdapter::toVehicleUsageInterval)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toList(); .toList();
return attachVehicleEvidence( return attachVehicleEvidence(
@ -277,8 +277,8 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
private Set<String> vehicleKeys(EventHubEventDto event) { private Set<String> vehicleKeys(EventHubEventDto event) {
LinkedHashSet<String> result = new LinkedHashSet<>(); LinkedHashSet<String> result = new LinkedHashSet<>();
String vehicleKey = TachographRuntimeIdentityResolver.vehicleKey(event); String vehicleKey = RuntimeEntityReferenceResolver.vehicleKey(event);
String registrationKey = TachographRuntimeIdentityResolver.registrationKey(event); String registrationKey = RuntimeEntityReferenceResolver.registrationKey(event);
add(result, vehicleKey); add(result, vehicleKey);
add(result, registrationKey); add(result, registrationKey);
if (vehicleKey != null) { if (vehicleKey != null) {
@ -316,22 +316,22 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
} }
} }
private List<ResolvedVehicleUsageInterval> mergeVehicleUsageIntervals(List<ResolvedVehicleUsageInterval> intervals) { private List<RuntimeVehicleUsageInterval> mergeVehicleUsageIntervals(List<RuntimeVehicleUsageInterval> intervals) {
if (intervals == null || intervals.isEmpty()) { if (intervals == null || intervals.isEmpty()) {
return List.of(); return List.of();
} }
List<ResolvedVehicleUsageInterval> sorted = intervals.stream() List<RuntimeVehicleUsageInterval> sorted = intervals.stream()
.sorted(Comparator.comparing(ResolvedVehicleUsageInterval::from, Comparator.nullsLast(Comparator.naturalOrder())) .sorted(Comparator.comparing(RuntimeVehicleUsageInterval::from, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(ResolvedVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder())) .thenComparing(RuntimeVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(ResolvedVehicleUsageInterval::intervalId, Comparator.nullsLast(String::compareTo))) .thenComparing(RuntimeVehicleUsageInterval::intervalId, Comparator.nullsLast(String::compareTo)))
.toList(); .toList();
List<ResolvedVehicleUsageInterval> merged = new ArrayList<>(); List<RuntimeVehicleUsageInterval> merged = new ArrayList<>();
for (ResolvedVehicleUsageInterval next : sorted) { for (RuntimeVehicleUsageInterval next : sorted) {
if (merged.isEmpty()) { if (merged.isEmpty()) {
merged.add(next); merged.add(next);
continue; continue;
} }
ResolvedVehicleUsageInterval current = merged.get(merged.size() - 1); RuntimeVehicleUsageInterval current = merged.get(merged.size() - 1);
if (canMerge(current, next)) { if (canMerge(current, next)) {
merged.set(merged.size() - 1, merge(current, next)); merged.set(merged.size() - 1, merge(current, next));
} else { } else {
@ -341,7 +341,7 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
return List.copyOf(merged); return List.copyOf(merged);
} }
private boolean canMerge(ResolvedVehicleUsageInterval left, ResolvedVehicleUsageInterval right) { private boolean canMerge(RuntimeVehicleUsageInterval left, RuntimeVehicleUsageInterval right) {
if (left == null || right == null || left.to() == null || right.from() == null) { if (left == null || right == null || left.to() == null || right.from() == null) {
return false; return false;
} }
@ -351,7 +351,7 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
&& !right.from().isAfter(left.to().plusSeconds(1)); && !right.from().isAfter(left.to().plusSeconds(1));
} }
private ResolvedVehicleUsageInterval merge(ResolvedVehicleUsageInterval left, ResolvedVehicleUsageInterval right) { private RuntimeVehicleUsageInterval merge(RuntimeVehicleUsageInterval left, RuntimeVehicleUsageInterval right) {
LinkedHashSet<String> sourceIntervalIds = new LinkedHashSet<>(); LinkedHashSet<String> sourceIntervalIds = new LinkedHashSet<>();
if (left.sourceIntervalIds() != null) { if (left.sourceIntervalIds() != null) {
sourceIntervalIds.addAll(left.sourceIntervalIds()); sourceIntervalIds.addAll(left.sourceIntervalIds());
@ -363,7 +363,7 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
if (right.to() != null && (end == null || right.to().isAfter(end))) { if (right.to() != null && (end == null || right.to().isAfter(end))) {
end = right.to(); end = right.to();
} }
return ResolvedVehicleUsageInterval.resolved( return RuntimeVehicleUsageInterval.resolved(
left.sessionId(), left.sessionId(),
left.driverKey(), left.driverKey(),
left.intervalId(), left.intervalId(),

View File

@ -11,7 +11,7 @@ import at.procon.eventhub.processing.model.RuntimeDriverVehicleEvidenceAttachmen
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest; import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
@ -276,7 +276,7 @@ public class RuntimeDriverWorkingTimeScopeProcessingService {
} }
private String driverKey(EventHubEventDto event) { private String driverKey(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.driverKey(event); return RuntimeEntityReferenceResolver.driverKey(event);
} }
private JsonNode rawPayload(EventHubEventDto event) { private JsonNode rawPayload(EventHubEventDto event) {

View File

@ -1,9 +1,10 @@
package at.procon.eventhub.processing.service; package at.procon.eventhub.processing.service;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest; import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily; import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.tachographfilesession.support.RuntimeTimelineCompatibilityAdapter;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession; import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession; import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInSessionException; import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInSessionException;
import at.procon.eventhub.tachographfilesession.service.EventBackedDriverTimelineBuilder; import at.procon.eventhub.tachographfilesession.service.EventBackedDriverTimelineBuilder;
@ -31,13 +32,15 @@ public class TachographFileSessionUnifiedDriverTimelineSource implements Unified
} }
@Override @Override
public ResolvedDriverTimeline loadDriverTimeline(UnifiedDriverTimelineRequest request) { public RuntimeDriverTimeline loadDriverTimeline(UnifiedDriverTimelineRequest request) {
TachographFileSession session = repository.find(request.sessionId()) TachographFileSession session = repository.find(request.sessionId())
.orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId())); .orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
DriverExtractionSession driver = session.driversByKey().get(request.driverKey()); DriverExtractionSession driver = session.driversByKey().get(request.driverKey());
if (driver == null) { if (driver == null) {
throw new DriverNotFoundInSessionException(request.sessionId(), request.driverKey()); throw new DriverNotFoundInSessionException(request.sessionId(), request.driverKey());
} }
return eventBackedDriverTimelineBuilder.build(session, driver); return RuntimeTimelineCompatibilityAdapter.fromResolvedDriverTimeline(
eventBackedDriverTimelineBuilder.build(session, driver)
);
} }
} }

View File

@ -1,7 +1,7 @@
package at.procon.eventhub.processing.service; package at.procon.eventhub.processing.service;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest; import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import java.util.List; import java.util.List;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -14,7 +14,7 @@ public class UnifiedDriverTimelineService {
this.timelineSources = List.copyOf(timelineSources); this.timelineSources = List.copyOf(timelineSources);
} }
public ResolvedDriverTimeline loadDriverTimeline(UnifiedDriverTimelineRequest request) { public RuntimeDriverTimeline loadDriverTimeline(UnifiedDriverTimelineRequest request) {
return timelineSources.stream() return timelineSources.stream()
.filter(source -> source.supports(request)) .filter(source -> source.supports(request))
.findFirst() .findFirst()

View File

@ -1,11 +1,11 @@
package at.procon.eventhub.processing.service; package at.procon.eventhub.processing.service;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest; import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
public interface UnifiedDriverTimelineSource { public interface UnifiedDriverTimelineSource {
boolean supports(UnifiedDriverTimelineRequest request); boolean supports(UnifiedDriverTimelineRequest request);
ResolvedDriverTimeline loadDriverTimeline(UnifiedDriverTimelineRequest request); RuntimeDriverTimeline loadDriverTimeline(UnifiedDriverTimelineRequest request);
} }

View File

@ -4,13 +4,13 @@ import at.procon.eventhub.dto.EventDomain;
import at.procon.eventhub.dto.EventHubEventDto; import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.dto.EventLifecycle; import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType; import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.processing.model.RuntimeActivityInterval;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeExtractionWarning;
import at.procon.eventhub.processing.model.RuntimeSupportEvent;
import at.procon.eventhub.processing.model.RuntimeVehicleUsageInterval;
import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
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 com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.Duration; import java.time.Duration;
@ -27,7 +27,7 @@ import org.springframework.stereotype.Service;
@Service @Service
public class UnifiedEventTimelineReconstructor { public class UnifiedEventTimelineReconstructor {
public ResolvedDriverTimeline reconstruct( public RuntimeDriverTimeline reconstruct(
UUID sessionId, UUID sessionId,
String driverKey, String driverKey,
List<EventHubEventDto> events List<EventHubEventDto> events
@ -35,23 +35,23 @@ public class UnifiedEventTimelineReconstructor {
return reconstruct(sessionId, driverKey, events, List.of(), null); return reconstruct(sessionId, driverKey, events, List.of(), null);
} }
public ResolvedDriverTimeline reconstruct( public RuntimeDriverTimeline reconstruct(
UUID sessionId, UUID sessionId,
String driverKey, String driverKey,
List<EventHubEventDto> events, List<EventHubEventDto> events,
List<ExtractionWarning> warnings, List<RuntimeExtractionWarning> warnings,
String sourceKind String sourceKind
) { ) {
List<EventHubEventDto> safeEvents = events == null ? List.of() : List.copyOf(events); List<EventHubEventDto> safeEvents = events == null ? List.of() : List.copyOf(events);
List<ResolvedActivityInterval> activityIntervals = reconstructActivityIntervals(safeEvents); List<RuntimeActivityInterval> activityIntervals = reconstructActivityIntervals(safeEvents);
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = List<RuntimeVehicleUsageInterval> vehicleUsageIntervals =
reconstructVehicleUsageIntervals(sessionId, driverKey, safeEvents); reconstructVehicleUsageIntervals(sessionId, driverKey, safeEvents);
List<ExtractedSupportEvent> supportEvents = reconstructSupportEvents(safeEvents); List<RuntimeSupportEvent> supportEvents = reconstructSupportEvents(safeEvents);
List<ExtractionWarning> mergedWarnings = warnings == null ? List.of() : List.copyOf(new LinkedHashSet<>(warnings)); List<RuntimeExtractionWarning> mergedWarnings = warnings == null ? List.of() : List.copyOf(new LinkedHashSet<>(warnings));
OffsetDateTime loadedFrom = minTimestamp(activityIntervals, vehicleUsageIntervals, supportEvents); OffsetDateTime loadedFrom = minTimestamp(activityIntervals, vehicleUsageIntervals, supportEvents);
OffsetDateTime loadedTo = maxTimestamp(activityIntervals, vehicleUsageIntervals, supportEvents); OffsetDateTime loadedTo = maxTimestamp(activityIntervals, vehicleUsageIntervals, supportEvents);
return new ResolvedDriverTimeline( return new RuntimeDriverTimeline(
resolveSourceKind(safeEvents, sourceKind), resolveSourceKind(safeEvents, sourceKind),
loadedFrom, loadedFrom,
loadedTo, loadedTo,
@ -62,7 +62,7 @@ public class UnifiedEventTimelineReconstructor {
); );
} }
private List<ResolvedActivityInterval> reconstructActivityIntervals(List<EventHubEventDto> events) { private List<RuntimeActivityInterval> reconstructActivityIntervals(List<EventHubEventDto> events) {
Map<String, ActivityAccumulator> byIntervalId = new LinkedHashMap<>(); Map<String, ActivityAccumulator> byIntervalId = new LinkedHashMap<>();
for (EventHubEventDto event : events) { for (EventHubEventDto event : events) {
if (event.eventDomain() != EventDomain.DRIVER_ACTIVITY) { if (event.eventDomain() != EventDomain.DRIVER_ACTIVITY) {
@ -83,13 +83,13 @@ public class UnifiedEventTimelineReconstructor {
return byIntervalId.values().stream() return byIntervalId.values().stream()
.map(ActivityAccumulator::finish) .map(ActivityAccumulator::finish)
.filter(interval -> interval != null) .filter(interval -> interval != null)
.sorted(Comparator.comparing(ResolvedActivityInterval::from) .sorted(Comparator.comparing(RuntimeActivityInterval::from)
.thenComparing(ResolvedActivityInterval::to) .thenComparing(RuntimeActivityInterval::to)
.thenComparing(ResolvedActivityInterval::activityType, Comparator.nullsLast(String::compareTo))) .thenComparing(RuntimeActivityInterval::activityType, Comparator.nullsLast(String::compareTo)))
.toList(); .toList();
} }
private List<ResolvedVehicleUsageInterval> reconstructVehicleUsageIntervals( private List<RuntimeVehicleUsageInterval> reconstructVehicleUsageIntervals(
UUID sessionId, UUID sessionId,
String driverKey, String driverKey,
List<EventHubEventDto> events List<EventHubEventDto> events
@ -117,13 +117,13 @@ public class UnifiedEventTimelineReconstructor {
return byIntervalId.values().stream() return byIntervalId.values().stream()
.map(VehicleUsageAccumulator::finish) .map(VehicleUsageAccumulator::finish)
.filter(interval -> interval != null) .filter(interval -> interval != null)
.sorted(Comparator.comparing(ResolvedVehicleUsageInterval::from) .sorted(Comparator.comparing(RuntimeVehicleUsageInterval::from)
.thenComparing(ResolvedVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder()))) .thenComparing(RuntimeVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder())))
.toList(); .toList();
} }
private List<ExtractedSupportEvent> reconstructSupportEvents(List<EventHubEventDto> events) { private List<RuntimeSupportEvent> reconstructSupportEvents(List<EventHubEventDto> events) {
List<ExtractedSupportEvent> result = new ArrayList<>(); List<RuntimeSupportEvent> result = new ArrayList<>();
for (EventHubEventDto event : events) { for (EventHubEventDto event : events) {
if (event.eventDomain() == EventDomain.DRIVER_ACTIVITY) { if (event.eventDomain() == EventDomain.DRIVER_ACTIVITY) {
continue; continue;
@ -136,16 +136,16 @@ public class UnifiedEventTimelineReconstructor {
String eventId = firstNonBlank(text(raw, "supportEventId"), event.externalSourceEventId()); String eventId = firstNonBlank(text(raw, "supportEventId"), event.externalSourceEventId());
BigDecimal latitude = event.position() == null ? null : event.position().latitude(); BigDecimal latitude = event.position() == null ? null : event.position().latitude();
BigDecimal longitude = event.position() == null ? null : event.position().longitude(); BigDecimal longitude = event.position() == null ? null : event.position().longitude();
result.add(new ExtractedSupportEvent( result.add(new RuntimeSupportEvent(
eventId, eventId,
TachographRuntimeIdentityResolver.driverKey(event), RuntimeEntityReferenceResolver.driverKey(event),
event.occurredAt(), event.occurredAt(),
event.eventDomain().name(), event.eventDomain().name(),
text(raw, "supportEventType") == null ? event.eventType().name() : text(raw, "supportEventType"), text(raw, "supportEventType") == null ? event.eventType().name() : text(raw, "supportEventType"),
event.lifecycle().name(), event.lifecycle().name(),
text(raw, "slot"), text(raw, "slot"),
TachographRuntimeIdentityResolver.registrationKey(event), RuntimeEntityReferenceResolver.registrationKey(event),
TachographRuntimeIdentityResolver.vehicleKey(event), RuntimeEntityReferenceResolver.vehicleKey(event),
firstNonBlank(text(raw, "country"), detailText(event, "country")), firstNonBlank(text(raw, "country"), detailText(event, "country")),
text(raw, "region"), text(raw, "region"),
firstNonBlank(text(raw, "countryFrom"), detailText(event, "countryFrom")), firstNonBlank(text(raw, "countryFrom"), detailText(event, "countryFrom")),
@ -161,9 +161,9 @@ public class UnifiedEventTimelineReconstructor {
text(raw, "rawRecordPath") text(raw, "rawRecordPath")
)); ));
} }
result.sort(Comparator.comparing(ExtractedSupportEvent::occurredAt) result.sort(Comparator.comparing(RuntimeSupportEvent::occurredAt)
.thenComparing(ExtractedSupportEvent::eventDomain, Comparator.nullsLast(String::compareTo)) .thenComparing(RuntimeSupportEvent::eventDomain, Comparator.nullsLast(String::compareTo))
.thenComparing(ExtractedSupportEvent::eventId, Comparator.nullsLast(String::compareTo))); .thenComparing(RuntimeSupportEvent::eventId, Comparator.nullsLast(String::compareTo)));
return List.copyOf(result); return List.copyOf(result);
} }
@ -189,36 +189,36 @@ public class UnifiedEventTimelineReconstructor {
} }
private OffsetDateTime minTimestamp( private OffsetDateTime minTimestamp(
List<ResolvedActivityInterval> activityIntervals, List<RuntimeActivityInterval> activityIntervals,
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals, List<RuntimeVehicleUsageInterval> vehicleUsageIntervals,
List<ExtractedSupportEvent> supportEvents List<RuntimeSupportEvent> supportEvents
) { ) {
OffsetDateTime min = null; OffsetDateTime min = null;
for (ResolvedActivityInterval interval : activityIntervals) { for (RuntimeActivityInterval interval : activityIntervals) {
min = min(min, interval.from()); min = min(min, interval.from());
} }
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) { for (RuntimeVehicleUsageInterval interval : vehicleUsageIntervals) {
min = min(min, interval.from()); min = min(min, interval.from());
} }
for (ExtractedSupportEvent supportEvent : supportEvents) { for (RuntimeSupportEvent supportEvent : supportEvents) {
min = min(min, supportEvent.occurredAt()); min = min(min, supportEvent.occurredAt());
} }
return min; return min;
} }
private OffsetDateTime maxTimestamp( private OffsetDateTime maxTimestamp(
List<ResolvedActivityInterval> activityIntervals, List<RuntimeActivityInterval> activityIntervals,
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals, List<RuntimeVehicleUsageInterval> vehicleUsageIntervals,
List<ExtractedSupportEvent> supportEvents List<RuntimeSupportEvent> supportEvents
) { ) {
OffsetDateTime max = null; OffsetDateTime max = null;
for (ResolvedActivityInterval interval : activityIntervals) { for (RuntimeActivityInterval interval : activityIntervals) {
max = max(max, interval.to()); max = max(max, interval.to());
} }
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) { for (RuntimeVehicleUsageInterval interval : vehicleUsageIntervals) {
max = max(max, interval.to()); max = max(max, interval.to());
} }
for (ExtractedSupportEvent supportEvent : supportEvents) { for (RuntimeSupportEvent supportEvent : supportEvents) {
max = max(max, supportEvent.occurredAt()); max = max(max, supportEvent.occurredAt());
} }
return max; return max;
@ -370,11 +370,11 @@ public class UnifiedEventTimelineReconstructor {
} }
} }
private ResolvedActivityInterval finish() { private RuntimeActivityInterval finish() {
if (sample == null || startedAt == null || endedAt == null || !endedAt.isAfter(startedAt)) { if (sample == null || startedAt == null || endedAt == null || !endedAt.isAfter(startedAt)) {
return null; return null;
} }
return new ResolvedActivityInterval( return new RuntimeActivityInterval(
intervalId, intervalId,
startedAt, startedAt,
endedAt, endedAt,
@ -383,8 +383,8 @@ public class UnifiedEventTimelineReconstructor {
detailText(sample, "cardSlot"), detailText(sample, "cardSlot"),
detailText(sample, "cardStatus"), detailText(sample, "cardStatus"),
detailText(sample, "drivingStatus"), detailText(sample, "drivingStatus"),
TachographRuntimeIdentityResolver.registrationKey(sample), RuntimeEntityReferenceResolver.registrationKey(sample),
TachographRuntimeIdentityResolver.vehicleKey(sample), RuntimeEntityReferenceResolver.vehicleKey(sample),
text(raw, "sourceKind"), text(raw, "sourceKind"),
stringList(raw, "sourceRowIds"), stringList(raw, "sourceRowIds"),
booleanValue(raw, "synthetic"), booleanValue(raw, "synthetic"),
@ -427,11 +427,11 @@ public class UnifiedEventTimelineReconstructor {
} }
} }
private ResolvedVehicleUsageInterval finish() { private RuntimeVehicleUsageInterval finish() {
if (startedAt == null) { if (startedAt == null) {
return null; return null;
} }
return ResolvedVehicleUsageInterval.resolved( return RuntimeVehicleUsageInterval.resolved(
sessionId, sessionId,
driverKey, driverKey,
intervalId, intervalId,
@ -439,8 +439,8 @@ public class UnifiedEventTimelineReconstructor {
endedAt, endedAt,
odometerBeginKm, odometerBeginKm,
odometerEndKm, odometerEndKm,
sample == null ? null : TachographRuntimeIdentityResolver.registrationKey(sample), sample == null ? null : RuntimeEntityReferenceResolver.registrationKey(sample),
sample == null ? null : TachographRuntimeIdentityResolver.vehicleKey(sample), sample == null ? null : RuntimeEntityReferenceResolver.vehicleKey(sample),
text(raw, "sourceKind"), text(raw, "sourceKind"),
stringList(raw, "sourceRowIds") stringList(raw, "sourceRowIds")
); );

View File

@ -13,14 +13,13 @@ import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTime
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeProcessingCore; import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeProcessingCore;
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeReusableProjectionBuilder; import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeReusableProjectionBuilder;
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceNormalizationResult; import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceNormalizationResult;
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceEvent;
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceNormalizer; import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceNormalizer;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeSupportEvent;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest; import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto; import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
import at.procon.eventhub.tachographfilesession.service.TachographDriverWorkingTimeAdapter; import at.procon.eventhub.processing.support.RuntimeDriverWorkingTimeAdapter;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent; import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent; import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle; import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
@ -31,7 +30,7 @@ import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialIn
import at.procon.eventhub.tachographfilesession.model.TachographEsperSupportGeoEvent; import at.procon.eventhub.tachographfilesession.model.TachographEsperSupportGeoEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent; import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent; import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import java.time.Duration; import java.time.Duration;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
@ -107,7 +106,7 @@ public class UnifiedRuntimeDerivedProjectionService {
eventBundle.mergedEvents() eventBundle.mergedEvents()
); );
List<EventHubEventDto> normalizedEvents = normalizationResult.normalizedEvents(); List<EventHubEventDto> normalizedEvents = normalizationResult.normalizedEvents();
ResolvedDriverTimeline timeline = timelineReconstructor.reconstruct( RuntimeDriverTimeline timeline = timelineReconstructor.reconstruct(
runtimeSessionId(request), runtimeSessionId(request),
driverKey, driverKey,
normalizedEvents normalizedEvents
@ -150,7 +149,7 @@ public class UnifiedRuntimeDerivedProjectionService {
significantDrivingMinutes, significantDrivingMinutes,
minimumRestPeriodMinutes, minimumRestPeriodMinutes,
timeline.activityIntervals().stream() timeline.activityIntervals().stream()
.map(interval -> TachographDriverWorkingTimeAdapter.toActivityInterval( .map(interval -> RuntimeDriverWorkingTimeAdapter.toActivityInterval(
runtimeSessionId(request), runtimeSessionId(request),
driverKey, driverKey,
interval interval
@ -158,11 +157,11 @@ public class UnifiedRuntimeDerivedProjectionService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toList(), .toList(),
timeline.vehicleUsageIntervals().stream() timeline.vehicleUsageIntervals().stream()
.map(TachographDriverWorkingTimeAdapter::toVehicleUsageInterval) .map(RuntimeDriverWorkingTimeAdapter::toVehicleUsageInterval)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toList(), .toList(),
timeline.supportEvents().stream() timeline.supportEvents().stream()
.map(this::toSupportEvidenceEvent) .map(RuntimeDriverWorkingTimeAdapter::toSupportEvidenceEvent)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toList(), .toList(),
notes notes
@ -207,41 +206,11 @@ public class UnifiedRuntimeDerivedProjectionService {
return request.sessionIds().size() == 1 ? request.sessionIds().get(0) : request.sessionId(); return request.sessionIds().size() == 1 ? request.sessionIds().get(0) : request.sessionId();
} }
private RuntimeSupportEvidenceEvent toSupportEvidenceEvent(ExtractedSupportEvent supportEvent) {
if (supportEvent == null || supportEvent.occurredAt() == null) {
return null;
}
return new RuntimeSupportEvidenceEvent(
supportEvent.eventId(),
null,
null,
supportEvent.eventDomain(),
supportEvent.eventType(),
supportEvent.eventLifecycle(),
supportEvent.driverKey(),
supportEvent.vehicleKey(),
supportEvent.registrationKey(),
supportEvent.occurredAt(),
supportEvent.occurredAt().toEpochSecond(),
supportEvent.latitude(),
supportEvent.longitude(),
supportEvent.country(),
supportEvent.region(),
supportEvent.countryFrom(),
supportEvent.countryTo(),
supportEvent.operation(),
supportEvent.odometerKm(),
supportEvent.avgSpeedKmh(),
supportEvent.maxSpeedKmh(),
java.util.Map.of("rawRecordPath", supportEvent.rawRecordPath())
);
}
private String resolveDriverKey( private String resolveDriverKey(
UnifiedRuntimeProcessingRequest request, UnifiedRuntimeProcessingRequest request,
List<EventHubEventDto> events List<EventHubEventDto> events
) { ) {
return TachographRuntimeIdentityResolver.requestDriverKey(request, events); return RuntimeEntityReferenceResolver.requestDriverKey(request, events);
} }
@ -436,12 +405,12 @@ public class UnifiedRuntimeDerivedProjectionService {
} }
private List<TachographEsperSupportGeoEvent> clipSupportGeoEvents( private List<TachographEsperSupportGeoEvent> clipSupportGeoEvents(
List<ExtractedSupportEvent> supportEvents, List<RuntimeSupportEvent> supportEvents,
String driverKey, String driverKey,
OffsetDateTime requestedFrom, OffsetDateTime requestedFrom,
OffsetDateTime requestedTo OffsetDateTime requestedTo
) { ) {
return (supportEvents == null ? List.<ExtractedSupportEvent>of() : supportEvents).stream() return (supportEvents == null ? List.<RuntimeSupportEvent>of() : supportEvents).stream()
.filter(event -> event.occurredAt() != null) .filter(event -> event.occurredAt() != null)
.filter(event -> driverKey == null || event.driverKey() == null || Objects.equals(driverKey, event.driverKey())) .filter(event -> driverKey == null || event.driverKey() == null || Objects.equals(driverKey, event.driverKey()))
.filter(event -> requestedFrom == null || !event.occurredAt().isBefore(requestedFrom)) .filter(event -> requestedFrom == null || !event.occurredAt().isBefore(requestedFrom))

View File

@ -1,10 +1,10 @@
package at.procon.eventhub.processing.service; package at.procon.eventhub.processing.service;
import at.procon.eventhub.dto.EventHubEventDto; import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest; import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver; import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import java.util.List; import java.util.List;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -22,7 +22,7 @@ public class UnifiedRuntimeDriverTimelineService {
this.timelineReconstructor = timelineReconstructor; this.timelineReconstructor = timelineReconstructor;
} }
public ResolvedDriverTimeline loadDriverTimeline(UnifiedRuntimeProcessingRequest request) { public RuntimeDriverTimeline loadDriverTimeline(UnifiedRuntimeProcessingRequest request) {
UnifiedRuntimeEventBundle bundle = runtimeEventAssemblyService.assembleDriverScopedEvents(request); UnifiedRuntimeEventBundle bundle = runtimeEventAssemblyService.assembleDriverScopedEvents(request);
return timelineReconstructor.reconstruct( return timelineReconstructor.reconstruct(
null, null,
@ -35,6 +35,6 @@ public class UnifiedRuntimeDriverTimelineService {
UnifiedRuntimeProcessingRequest request, UnifiedRuntimeProcessingRequest request,
List<EventHubEventDto> events List<EventHubEventDto> events
) { ) {
return TachographRuntimeIdentityResolver.requestDriverKey(request, events); return RuntimeEntityReferenceResolver.requestDriverKey(request, events);
} }
} }

View File

@ -0,0 +1,123 @@
package at.procon.eventhub.processing.support;
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceEvent;
import at.procon.eventhub.processing.model.RuntimeActivityInterval;
import at.procon.eventhub.processing.model.RuntimeSupportEvent;
import at.procon.eventhub.processing.model.RuntimeVehicleUsageInterval;
import java.util.List;
import java.util.UUID;
public final class RuntimeDriverWorkingTimeAdapter {
private RuntimeDriverWorkingTimeAdapter() {
}
public static DriverWorkingTimeActivityInterval toActivityInterval(
UUID sessionId,
String driverKey,
RuntimeActivityInterval interval
) {
if (interval == null || interval.from() == null || interval.to() == null) {
return null;
}
return new DriverWorkingTimeActivityInterval(
sessionId,
driverKey,
interval.intervalId(),
interval.activityType(),
interval.slot(),
interval.cardStatus(),
interval.drivingStatus(),
interval.registrationKey(),
interval.vehicleKey(),
interval.sourceKind(),
firstSourceIntervalId(interval),
lastSourceIntervalId(interval),
interval.from(),
interval.to(),
interval.from().toEpochSecond(),
interval.to().toEpochSecond(),
interval.durationSeconds(),
interval.sourceIntervalIds(),
interval.synthetic(),
interval.clippedToRequestedPeriod(),
interval.level()
);
}
public static DriverWorkingTimeVehicleUsageInterval toVehicleUsageInterval(RuntimeVehicleUsageInterval interval) {
if (interval == null || interval.from() == null) {
return null;
}
return new DriverWorkingTimeVehicleUsageInterval(
interval.sessionId(),
interval.driverKey(),
interval.intervalId(),
firstSourceIntervalId(interval),
lastSourceIntervalId(interval),
interval.from(),
interval.to(),
interval.from().toEpochSecond(),
interval.to() == null ? null : interval.to().toEpochSecond(),
interval.durationSeconds(),
interval.odometerBeginKm(),
interval.odometerEndKm(),
interval.registrationKey(),
interval.vehicleKey(),
interval.sourceKind(),
interval.sourceIntervalIds()
);
}
public static RuntimeSupportEvidenceEvent toSupportEvidenceEvent(RuntimeSupportEvent supportEvent) {
if (supportEvent == null || supportEvent.occurredAt() == null) {
return null;
}
return new RuntimeSupportEvidenceEvent(
supportEvent.eventId(),
null,
null,
supportEvent.eventDomain(),
supportEvent.eventType(),
supportEvent.eventLifecycle(),
supportEvent.driverKey(),
supportEvent.vehicleKey(),
supportEvent.registrationKey(),
supportEvent.occurredAt(),
supportEvent.occurredAt().toEpochSecond(),
supportEvent.latitude(),
supportEvent.longitude(),
supportEvent.country(),
supportEvent.region(),
supportEvent.countryFrom(),
supportEvent.countryTo(),
supportEvent.operation(),
supportEvent.odometerKm(),
supportEvent.avgSpeedKmh(),
supportEvent.maxSpeedKmh(),
java.util.Map.of("rawRecordPath", supportEvent.rawRecordPath())
);
}
private static String firstSourceIntervalId(RuntimeActivityInterval interval) {
return safe(interval.sourceIntervalIds()).isEmpty() ? interval.intervalId() : safe(interval.sourceIntervalIds()).getFirst();
}
private static String lastSourceIntervalId(RuntimeActivityInterval interval) {
return safe(interval.sourceIntervalIds()).isEmpty() ? interval.intervalId() : safe(interval.sourceIntervalIds()).getLast();
}
private static String firstSourceIntervalId(RuntimeVehicleUsageInterval interval) {
return safe(interval.sourceIntervalIds()).isEmpty() ? interval.intervalId() : safe(interval.sourceIntervalIds()).getFirst();
}
private static String lastSourceIntervalId(RuntimeVehicleUsageInterval interval) {
return safe(interval.sourceIntervalIds()).isEmpty() ? interval.intervalId() : safe(interval.sourceIntervalIds()).getLast();
}
private static <T> List<T> safe(List<T> values) {
return values == null ? List.of() : values;
}
}

View File

@ -9,9 +9,9 @@ import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.util.List; import java.util.List;
public final class TachographRuntimeIdentityResolver { public final class RuntimeEntityReferenceResolver {
private TachographRuntimeIdentityResolver() { private RuntimeEntityReferenceResolver() {
} }
public static String driverKey(EventHubEventDto event) { public static String driverKey(EventHubEventDto event) {

View File

@ -29,9 +29,9 @@ public final class RuntimeEventIdentityResolver {
parts.add(nullToEmpty(event.eventType() == null ? null : event.eventType().name())); parts.add(nullToEmpty(event.eventType() == null ? null : event.eventType().name()));
parts.add(nullToEmpty(event.lifecycle() == null ? null : event.lifecycle().name())); parts.add(nullToEmpty(event.lifecycle() == null ? null : event.lifecycle().name()));
parts.add(normalizeTime(event.occurredAt())); parts.add(normalizeTime(event.occurredAt()));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.driverKey(event))); parts.add(nullToEmpty(RuntimeEntityReferenceResolver.driverKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.registrationKey(event))); parts.add(nullToEmpty(RuntimeEntityReferenceResolver.registrationKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.vehicleKey(event))); parts.add(nullToEmpty(RuntimeEntityReferenceResolver.vehicleKey(event)));
parts.add(isIntervalPointEvent(event) ? nullToEmpty(runtimeIntervalKey(event)) : ""); parts.add(isIntervalPointEvent(event) ? nullToEmpty(runtimeIntervalKey(event)) : "");
parts.add(nullToEmpty(firstNonBlank(text(raw, "activityType"), event.eventType() == null ? null : event.eventType().name()))); parts.add(nullToEmpty(firstNonBlank(text(raw, "activityType"), event.eventType() == null ? null : event.eventType().name())));
parts.add(nullToEmpty(firstNonBlank(text(raw, "slot"), text(raw, "cardSlot"), text(details, "cardSlot")))); parts.add(nullToEmpty(firstNonBlank(text(raw, "slot"), text(raw, "cardSlot"), text(details, "cardSlot"))));
@ -72,9 +72,9 @@ public final class RuntimeEventIdentityResolver {
parts.add("INTERVAL"); parts.add("INTERVAL");
parts.add(nullToEmpty(event.eventDomain() == null ? null : event.eventDomain().name())); parts.add(nullToEmpty(event.eventDomain() == null ? null : event.eventDomain().name()));
parts.add(nullToEmpty(intervalSemanticType(event, raw))); parts.add(nullToEmpty(intervalSemanticType(event, raw)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.driverKey(event))); parts.add(nullToEmpty(RuntimeEntityReferenceResolver.driverKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.registrationKey(event))); parts.add(nullToEmpty(RuntimeEntityReferenceResolver.registrationKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.vehicleKey(event))); parts.add(nullToEmpty(RuntimeEntityReferenceResolver.vehicleKey(event)));
parts.add(nullToEmpty(intervalCardSlot(event, raw, details))); parts.add(nullToEmpty(intervalCardSlot(event, raw, details)));
parts.add(nullToEmpty(intervalCardStatus(event, raw, details))); parts.add(nullToEmpty(intervalCardStatus(event, raw, details)));
parts.add(nullToEmpty(intervalDrivingStatus(event, raw, details))); parts.add(nullToEmpty(intervalDrivingStatus(event, raw, details)));
@ -152,7 +152,7 @@ public final class RuntimeEventIdentityResolver {
} }
private static JsonNode rawPayload(EventHubEventDto event) { private static JsonNode rawPayload(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.rawPayload(event); return RuntimeEntityReferenceResolver.rawPayload(event);
} }
private static JsonNode detailAttributes(EventHubEventDto event) { private static JsonNode detailAttributes(EventHubEventDto event) {

View File

@ -1,5 +1,6 @@
package at.procon.eventhub.tachographfilesession.service; package at.procon.eventhub.tachographfilesession.service;
import at.procon.eventhub.tachographfilesession.support.RuntimeTimelineCompatibilityAdapter;
import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor; import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession; import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.ExtractionWarning; import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
@ -35,12 +36,16 @@ public class EventBackedDriverTimelineBuilder {
DriverExtractionSession driverSession DriverExtractionSession driverSession
) { ) {
TachographTimelineEventBundle bundle = eventBuilder.buildEventBundle(session, driverSession); TachographTimelineEventBundle bundle = eventBuilder.buildEventBundle(session, driverSession);
return timelineReconstructor.reconstruct( return RuntimeTimelineCompatibilityAdapter.toResolvedDriverTimeline(
session.sessionId(), timelineReconstructor.reconstruct(
driverSession.driverKey(), session.sessionId(),
bundle.allEvents(), driverSession.driverKey(),
mergeWarnings(session.warnings(), driverSession.warnings()), bundle.allEvents(),
fallbackSourceKind(session) mergeWarnings(session.warnings(), driverSession.warnings()).stream()
.map(RuntimeTimelineCompatibilityAdapter::fromExtractionWarning)
.toList(),
fallbackSourceKind(session)
)
); );
} }

View File

@ -2,6 +2,7 @@ package at.procon.eventhub.tachographfilesession.service;
import at.procon.eventhub.dto.EventHubEventDto; import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor; import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.tachographfilesession.support.RuntimeTimelineCompatibilityAdapter;
import at.procon.eventhub.service.EventAcquisitionRecordKeyService; import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
import at.procon.eventhub.service.EventHubEventSorter; import at.procon.eventhub.service.EventHubEventSorter;
import at.procon.eventhub.tachographfilesession.dto.CreateTachographCompositeSessionRequest; import at.procon.eventhub.tachographfilesession.dto.CreateTachographCompositeSessionRequest;
@ -119,12 +120,16 @@ public class TachographCompositeSessionService {
throw new DriverNotFoundInCompositeSessionException(compositeSessionId, driverKey); throw new DriverNotFoundInCompositeSessionException(compositeSessionId, driverKey);
} }
List<EventHubEventDto> mergedEvents = mergeDriverEvents(sourceSessions, driverKey); List<EventHubEventDto> mergedEvents = mergeDriverEvents(sourceSessions, driverKey);
return timelineReconstructor.reconstruct( return RuntimeTimelineCompatibilityAdapter.toResolvedDriverTimeline(
compositeSessionId, timelineReconstructor.reconstruct(
driverKey, compositeSessionId,
mergedEvents, driverKey,
mergeWarnings(sourceSessions, driverKey), mergedEvents,
"COMPOSITE_TACHOGRAPH_FILE_SESSION" mergeWarnings(sourceSessions, driverKey).stream()
.map(RuntimeTimelineCompatibilityAdapter::fromExtractionWarning)
.toList(),
"COMPOSITE_TACHOGRAPH_FILE_SESSION"
)
); );
} }

View File

@ -3,6 +3,7 @@ package at.procon.eventhub.tachographfilesession.service;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import at.procon.eventhub.dto.EventHubEventDto; import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor; import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.tachographfilesession.support.RuntimeTimelineCompatibilityAdapter;
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 java.time.OffsetDateTime; import java.time.OffsetDateTime;
@ -51,10 +52,12 @@ public class TachographEventTimelineReconstructionHelper {
String fallbackDriverKey, String fallbackDriverKey,
List<EventHubEventDto> events List<EventHubEventDto> events
) { ) {
ResolvedDriverTimeline reconstructed = timelineReconstructor.reconstruct( ResolvedDriverTimeline reconstructed = RuntimeTimelineCompatibilityAdapter.toResolvedDriverTimeline(
fallbackSessionId == null ? new UUID(0L, 0L) : fallbackSessionId, timelineReconstructor.reconstruct(
fallbackDriverKey, fallbackSessionId == null ? new UUID(0L, 0L) : fallbackSessionId,
safe(events) fallbackDriverKey,
safe(events)
)
); );
return new ResolvedDriverTimeline( return new ResolvedDriverTimeline(
reconstructed.sourceKind(), reconstructed.sourceKind(),

View File

@ -0,0 +1,237 @@
package at.procon.eventhub.tachographfilesession.support;
import at.procon.eventhub.processing.model.RuntimeActivityInterval;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeExtractionWarning;
import at.procon.eventhub.processing.model.RuntimeSupportEvent;
import at.procon.eventhub.processing.model.RuntimeVehicleUsageInterval;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
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 java.util.List;
public final class RuntimeTimelineCompatibilityAdapter {
private RuntimeTimelineCompatibilityAdapter() {
}
public static RuntimeDriverTimeline fromResolvedDriverTimeline(ResolvedDriverTimeline timeline) {
if (timeline == null) {
return null;
}
return new RuntimeDriverTimeline(
timeline.sourceKind(),
timeline.loadedFrom(),
timeline.loadedTo(),
safe(timeline.vehicleUsageIntervals()).stream()
.map(RuntimeTimelineCompatibilityAdapter::fromResolvedVehicleUsageInterval)
.toList(),
safe(timeline.activityIntervals()).stream()
.map(RuntimeTimelineCompatibilityAdapter::fromResolvedActivityInterval)
.toList(),
safe(timeline.supportEvents()).stream()
.map(RuntimeTimelineCompatibilityAdapter::fromExtractedSupportEvent)
.toList(),
safe(timeline.warnings()).stream()
.map(RuntimeTimelineCompatibilityAdapter::fromExtractionWarning)
.toList()
);
}
public static ResolvedDriverTimeline toResolvedDriverTimeline(RuntimeDriverTimeline timeline) {
if (timeline == null) {
return null;
}
return new ResolvedDriverTimeline(
timeline.sourceKind(),
timeline.loadedFrom(),
timeline.loadedTo(),
safe(timeline.vehicleUsageIntervals()).stream()
.map(RuntimeTimelineCompatibilityAdapter::toResolvedVehicleUsageInterval)
.toList(),
safe(timeline.activityIntervals()).stream()
.map(RuntimeTimelineCompatibilityAdapter::toResolvedActivityInterval)
.toList(),
safe(timeline.supportEvents()).stream()
.map(RuntimeTimelineCompatibilityAdapter::toExtractedSupportEvent)
.toList(),
safe(timeline.warnings()).stream()
.map(RuntimeTimelineCompatibilityAdapter::toExtractionWarning)
.toList()
);
}
public static RuntimeActivityInterval fromResolvedActivityInterval(ResolvedActivityInterval interval) {
if (interval == null) {
return null;
}
return new RuntimeActivityInterval(
interval.intervalId(),
interval.from(),
interval.to(),
interval.durationSeconds(),
interval.activityType(),
interval.slot(),
interval.cardStatus(),
interval.drivingStatus(),
interval.registrationKey(),
interval.vehicleKey(),
interval.sourceKind(),
interval.sourceIntervalIds(),
interval.synthetic(),
interval.clippedToRequestedPeriod(),
interval.level()
);
}
public static ResolvedActivityInterval toResolvedActivityInterval(RuntimeActivityInterval interval) {
if (interval == null) {
return null;
}
return new ResolvedActivityInterval(
interval.intervalId(),
interval.from(),
interval.to(),
interval.durationSeconds(),
interval.activityType(),
interval.slot(),
interval.cardStatus(),
interval.drivingStatus(),
interval.registrationKey(),
interval.vehicleKey(),
interval.sourceKind(),
interval.sourceIntervalIds(),
interval.synthetic(),
interval.clippedToRequestedPeriod(),
interval.level()
);
}
public static RuntimeVehicleUsageInterval fromResolvedVehicleUsageInterval(ResolvedVehicleUsageInterval interval) {
if (interval == null) {
return null;
}
return new RuntimeVehicleUsageInterval(
interval.sessionId(),
interval.driverKey(),
interval.intervalId(),
interval.from(),
interval.to(),
interval.durationSeconds(),
interval.odometerBeginKm(),
interval.odometerEndKm(),
interval.registrationKey(),
interval.vehicleKey(),
interval.sourceKind(),
interval.sourceIntervalIds()
);
}
public static ResolvedVehicleUsageInterval toResolvedVehicleUsageInterval(RuntimeVehicleUsageInterval interval) {
if (interval == null) {
return null;
}
return new ResolvedVehicleUsageInterval(
interval.sessionId(),
interval.driverKey(),
interval.intervalId(),
interval.from(),
interval.to(),
interval.durationSeconds(),
interval.odometerBeginKm(),
interval.odometerEndKm(),
interval.registrationKey(),
interval.vehicleKey(),
interval.sourceKind(),
interval.sourceIntervalIds()
);
}
public static RuntimeSupportEvent fromExtractedSupportEvent(ExtractedSupportEvent supportEvent) {
if (supportEvent == null) {
return null;
}
return new RuntimeSupportEvent(
supportEvent.eventId(),
supportEvent.driverKey(),
supportEvent.occurredAt(),
supportEvent.eventDomain(),
supportEvent.eventType(),
supportEvent.eventLifecycle(),
supportEvent.slot(),
supportEvent.registrationKey(),
supportEvent.vehicleKey(),
supportEvent.country(),
supportEvent.region(),
supportEvent.countryFrom(),
supportEvent.countryTo(),
supportEvent.operation(),
supportEvent.latitude(),
supportEvent.longitude(),
supportEvent.authenticationStatus(),
supportEvent.odometerKm(),
supportEvent.code(),
supportEvent.avgSpeedKmh(),
supportEvent.maxSpeedKmh(),
supportEvent.rawRecordPath()
);
}
public static ExtractedSupportEvent toExtractedSupportEvent(RuntimeSupportEvent supportEvent) {
if (supportEvent == null) {
return null;
}
return new ExtractedSupportEvent(
supportEvent.eventId(),
supportEvent.driverKey(),
supportEvent.occurredAt(),
supportEvent.eventDomain(),
supportEvent.eventType(),
supportEvent.eventLifecycle(),
supportEvent.slot(),
supportEvent.registrationKey(),
supportEvent.vehicleKey(),
supportEvent.country(),
supportEvent.region(),
supportEvent.countryFrom(),
supportEvent.countryTo(),
supportEvent.operation(),
supportEvent.latitude(),
supportEvent.longitude(),
supportEvent.authenticationStatus(),
supportEvent.odometerKm(),
supportEvent.code(),
supportEvent.avgSpeedKmh(),
supportEvent.maxSpeedKmh(),
supportEvent.rawRecordPath()
);
}
public static RuntimeExtractionWarning fromExtractionWarning(ExtractionWarning warning) {
if (warning == null) {
return null;
}
return new RuntimeExtractionWarning(
warning.code(),
warning.message(),
warning.rawRecordPath()
);
}
public static ExtractionWarning toExtractionWarning(RuntimeExtractionWarning warning) {
if (warning == null) {
return null;
}
return new ExtractionWarning(
warning.code(),
warning.message(),
warning.rawRecordPath()
);
}
private static <T> List<T> safe(List<T> values) {
return values == null ? List.of() : values;
}
}

View File

@ -33,14 +33,14 @@ import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily; import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.model.RuntimeActivityInterval;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeVehicleUsageInterval;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest; import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.service.UnifiedRuntimeDerivedProjectionService; import at.procon.eventhub.processing.service.UnifiedRuntimeDerivedProjectionService;
import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService; import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService;
import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService; import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService;
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto; import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -123,11 +123,11 @@ class UnifiedRuntimeProcessingControllerTest {
.build(); .build();
when(timelineService.loadDriverTimeline(any())) when(timelineService.loadDriverTimeline(any()))
.thenReturn(new ResolvedDriverTimeline( .thenReturn(new RuntimeDriverTimeline(
"UNIFIED_EVENT_STREAM", "UNIFIED_EVENT_STREAM",
OffsetDateTime.parse("2026-05-01T08:00:00Z"), OffsetDateTime.parse("2026-05-01T08:00:00Z"),
OffsetDateTime.parse("2026-05-01T10:00:00Z"), OffsetDateTime.parse("2026-05-01T10:00:00Z"),
List.of(ResolvedVehicleUsageInterval.resolved( List.of(RuntimeVehicleUsageInterval.resolved(
null, null,
"DRIVER:42", "DRIVER:42",
"CVU-1", "CVU-1",
@ -140,7 +140,7 @@ class UnifiedRuntimeProcessingControllerTest {
"DRIVER_CARD", "DRIVER_CARD",
List.of("CVU-1") List.of("CVU-1")
)), )),
List.of(new ResolvedActivityInterval( List.of(new RuntimeActivityInterval(
"ACT-1", "ACT-1",
OffsetDateTime.parse("2026-05-01T08:30:00Z"), OffsetDateTime.parse("2026-05-01T08:30:00Z"),
OffsetDateTime.parse("2026-05-01T09:00:00Z"), OffsetDateTime.parse("2026-05-01T09:00:00Z"),
@ -181,7 +181,6 @@ class UnifiedRuntimeProcessingControllerTest {
.andExpect(jsonPath("$.vehicleUsageIntervals[0].intervalId").value("CVU-1")); .andExpect(jsonPath("$.vehicleUsageIntervals[0].intervalId").value("CVU-1"));
} }
@Test @Test
void loadsDriverDerivedProjectionsViaRuntimeApi() throws Exception { void loadsDriverDerivedProjectionsViaRuntimeApi() throws Exception {
UnifiedRuntimeEventAssemblyService eventAssemblyService = org.mockito.Mockito.mock(UnifiedRuntimeEventAssemblyService.class); UnifiedRuntimeEventAssemblyService eventAssemblyService = org.mockito.Mockito.mock(UnifiedRuntimeEventAssemblyService.class);

View File

@ -3,6 +3,7 @@ package at.procon.eventhub.processing.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.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest; import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest;
import at.procon.eventhub.service.EventDetailsFactory; import at.procon.eventhub.service.EventDetailsFactory;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession; import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
@ -14,7 +15,6 @@ import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
import at.procon.eventhub.tachographfilesession.model.ExtractedVehicle; import at.procon.eventhub.tachographfilesession.model.ExtractedVehicle;
import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration; import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration;
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.TachographFileSession; import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata; import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import at.procon.eventhub.tachographfilesession.service.DriverKeyFactory; import at.procon.eventhub.tachographfilesession.service.DriverKeyFactory;
@ -55,7 +55,7 @@ class UnifiedDriverTimelineServiceTest {
TachographFileSession session = session(driver); TachographFileSession session = session(driver);
repository.save(session); repository.save(session);
ResolvedDriverTimeline timeline = service.loadDriverTimeline( RuntimeDriverTimeline timeline = service.loadDriverTimeline(
UnifiedDriverTimelineRequest.forTachographFileSession(session.sessionId(), driver.driverKey()) UnifiedDriverTimelineRequest.forTachographFileSession(session.sessionId(), driver.driverKey())
); );

View File

@ -14,7 +14,7 @@ import at.procon.eventhub.dto.GeoPointDto;
import at.procon.eventhub.dto.ImportScopeDto; import at.procon.eventhub.dto.ImportScopeDto;
import at.procon.eventhub.dto.VehicleRefDto; import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.dto.VehicleRegistrationRefDto; import at.procon.eventhub.dto.VehicleRegistrationRefDto;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline; import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -38,7 +38,7 @@ class UnifiedEventTimelineReconstructorTest {
supportEvent("SUP-1", "2026-05-01T08:30:00Z") supportEvent("SUP-1", "2026-05-01T08:30:00Z")
); );
ResolvedDriverTimeline timeline = reconstructor.reconstruct( RuntimeDriverTimeline timeline = reconstructor.reconstruct(
UUID.randomUUID(), UUID.randomUUID(),
"DRIVER:42", "DRIVER:42",
events events
@ -68,7 +68,7 @@ class UnifiedEventTimelineReconstructorTest {
activityEvent("VU-ACT-99", EventLifecycle.END, "2026-05-01T09:00:00Z", "2026-05-01T08:00:00Z", "2026-05-01T09:00:00Z") activityEvent("VU-ACT-99", EventLifecycle.END, "2026-05-01T09:00:00Z", "2026-05-01T08:00:00Z", "2026-05-01T09:00:00Z")
); );
ResolvedDriverTimeline timeline = reconstructor.reconstruct( RuntimeDriverTimeline timeline = reconstructor.reconstruct(
UUID.randomUUID(), UUID.randomUUID(),
"DRIVER:42", "DRIVER:42",
events events
@ -105,7 +105,7 @@ class UnifiedEventTimelineReconstructorTest {
) )
); );
ResolvedDriverTimeline timeline = reconstructor.reconstruct( RuntimeDriverTimeline timeline = reconstructor.reconstruct(
UUID.randomUUID(), UUID.randomUUID(),
"DRIVER:42", "DRIVER:42",
events events

View File

@ -16,8 +16,8 @@ import at.procon.eventhub.dto.VehicleRegistrationRefDto;
import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef; import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily; import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend; import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest; import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import java.time.LocalDate; import java.time.LocalDate;
@ -40,7 +40,7 @@ class UnifiedRuntimeDriverTimelineServiceTest {
new UnifiedEventTimelineReconstructor() new UnifiedEventTimelineReconstructor()
); );
ResolvedDriverTimeline timeline = service.loadDriverTimeline( RuntimeDriverTimeline timeline = service.loadDriverTimeline(
new UnifiedRuntimeProcessingRequest( new UnifiedRuntimeProcessingRequest(
null, null,
List.of(), List.of(),

View File

@ -15,6 +15,8 @@ import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType; import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.dto.VehicleRefDto; import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.dto.VehicleRegistrationRefDto; import at.procon.eventhub.dto.VehicleRegistrationRefDto;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeExtractionWarning;
import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor; import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.service.EventAcquisitionRecordKeyService; import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
import at.procon.eventhub.service.EventHubEventSorter; import at.procon.eventhub.service.EventHubEventSorter;
@ -175,7 +177,7 @@ class TachographCompositeSessionServiceTest {
when(eventBuilder.buildEvents(first, firstDriver)).thenReturn(List.of(event("a", occurredAt, EventType.WORK))); when(eventBuilder.buildEvents(first, firstDriver)).thenReturn(List.of(event("a", occurredAt, EventType.WORK)));
when(eventBuilder.buildEvents(second, secondDriver)).thenReturn(List.of(event("b", occurredAt.plusHours(1), EventType.DRIVE))); when(eventBuilder.buildEvents(second, secondDriver)).thenReturn(List.of(event("b", occurredAt.plusHours(1), EventType.DRIVE)));
ResolvedDriverTimeline expected = new ResolvedDriverTimeline( RuntimeDriverTimeline reconstructed = new RuntimeDriverTimeline(
"COMPOSITE_TACHOGRAPH_FILE_SESSION", "COMPOSITE_TACHOGRAPH_FILE_SESSION",
occurredAt, occurredAt,
occurredAt.plusHours(1), occurredAt.plusHours(1),
@ -185,14 +187,22 @@ class TachographCompositeSessionServiceTest {
List.of() List.of()
); );
when(reconstructor.reconstruct(any(), eq("12:123"), any(), any(), eq("COMPOSITE_TACHOGRAPH_FILE_SESSION"))) when(reconstructor.reconstruct(any(), eq("12:123"), any(), any(), eq("COMPOSITE_TACHOGRAPH_FILE_SESSION")))
.thenReturn(expected); .thenReturn(reconstructed);
ResolvedDriverTimeline actual = ResolvedDriverTimeline actual =
service.getMergedDriverTimeline(created.session().compositeSessionId(), "12:123"); service.getMergedDriverTimeline(created.session().compositeSessionId(), "12:123");
assertThat(actual).isSameAs(expected); assertThat(actual).isEqualTo(new ResolvedDriverTimeline(
"COMPOSITE_TACHOGRAPH_FILE_SESSION",
occurredAt,
occurredAt.plusHours(1),
List.of(),
List.of(),
List.of(),
List.of()
));
ArgumentCaptor<List<EventHubEventDto>> eventsCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor<List<EventHubEventDto>> eventsCaptor = ArgumentCaptor.forClass(List.class);
ArgumentCaptor<List<ExtractionWarning>> warningsCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor<List<RuntimeExtractionWarning>> warningsCaptor = ArgumentCaptor.forClass(List.class);
verify(reconstructor).reconstruct( verify(reconstructor).reconstruct(
eq(created.session().compositeSessionId()), eq(created.session().compositeSessionId()),
eq("12:123"), eq("12:123"),
@ -203,10 +213,10 @@ class TachographCompositeSessionServiceTest {
assertThat(eventsCaptor.getValue()).extracting(EventHubEventDto::externalSourceEventId) assertThat(eventsCaptor.getValue()).extracting(EventHubEventDto::externalSourceEventId)
.containsExactly("a", "b"); .containsExactly("a", "b");
assertThat(warningsCaptor.getValue()).containsExactly( assertThat(warningsCaptor.getValue()).containsExactly(
new ExtractionWarning("SESSION_A", "warn-a", "s1"), new RuntimeExtractionWarning("SESSION_A", "warn-a", "s1"),
new ExtractionWarning("CARD_GAP", "gap", "p1"), new RuntimeExtractionWarning("CARD_GAP", "gap", "p1"),
new ExtractionWarning("SESSION_B", "warn-b", "s2"), new RuntimeExtractionWarning("SESSION_B", "warn-b", "s2"),
new ExtractionWarning("DUPLICATE", "duplicate", "p2") new RuntimeExtractionWarning("DUPLICATE", "duplicate", "p2")
); );
} }