Compare commits

...

3 Commits

Author SHA1 Message Date
trifonovt 84922f6c44 Generalize runtime source-kind filtering 2026-06-05 15:58:42 +02:00
trifonovt 5cf0cedc41 Decouple runtime timelines from tachograph models 2026-06-05 15:58:41 +02:00
trifonovt aa360c9f02 Decouple runtime event window bundle selection 2026-06-05 15:58:41 +02:00
42 changed files with 871 additions and 276 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.RuntimeTachographParityValidationResultDto;
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.service.UnifiedRuntimeDerivedProjectionService;
import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService;
import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService;
import at.procon.eventhub.processing.service.RuntimeDriverWorkingTimeScopeProcessingService;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
@ -106,7 +106,7 @@ public class UnifiedRuntimeProcessingController {
}
@PostMapping("/driver-timeline")
public ResponseEntity<ResolvedDriverTimeline> loadDriverTimeline(
public ResponseEntity<RuntimeDriverTimeline> loadDriverTimeline(
@RequestBody UnifiedRuntimeProcessingApiRequest request
) {
return ResponseEntity.ok(runtimeDriverTimelineService.loadDriverTimeline(request.toRuntimeRequest()));

View File

@ -1,7 +1,7 @@
package at.procon.eventhub.processing.dto;
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;
public record RuntimeVehicleUsageIntervalDebugDto(
@ -12,7 +12,7 @@ public record RuntimeVehicleUsageIntervalDebugDto(
String vehicleKey,
String sourceKind
) {
public static RuntimeVehicleUsageIntervalDebugDto from(ResolvedVehicleUsageInterval interval) {
public static RuntimeVehicleUsageIntervalDebugDto from(RuntimeVehicleUsageInterval interval) {
if (interval == null) {
return null;
}

View File

@ -3,7 +3,6 @@ package at.procon.eventhub.processing.dto;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.model.UnifiedTachographSourceKind;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Set;
@ -16,7 +15,7 @@ public record UnifiedRuntimeProcessingApiRequest(
String tenantKey,
Set<UnifiedEventSourceFamily> sourceFamilies,
UnifiedRuntimeEventBackend eventBackend,
Set<UnifiedTachographSourceKind> tachographSourceKinds,
Set<String> sourceKinds,
String driverKey,
Set<String> driverKeys,
Boolean includeAllDrivers,
@ -43,7 +42,7 @@ public record UnifiedRuntimeProcessingApiRequest(
String tenantKey,
Set<UnifiedEventSourceFamily> sourceFamilies,
UnifiedRuntimeEventBackend eventBackend,
Set<UnifiedTachographSourceKind> tachographSourceKinds,
Set<String> sourceKinds,
String driverKey,
Set<String> driverKeys,
Boolean includeAllDrivers,
@ -69,7 +68,7 @@ public record UnifiedRuntimeProcessingApiRequest(
tenantKey,
sourceFamilies,
eventBackend,
tachographSourceKinds,
sourceKinds,
driverKey,
driverKeys,
includeAllDrivers,
@ -99,7 +98,7 @@ public record UnifiedRuntimeProcessingApiRequest(
tenantKey,
sourceFamilies,
eventBackend,
tachographSourceKinds,
sourceKinds,
driverKey,
driverKeys,
includeAllDrivers != null && includeAllDrivers,

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.service.RuntimeDriverVehicleEvidenceAttachmentService;
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 java.util.ArrayList;
import java.util.Comparator;
@ -260,7 +260,7 @@ public class VehicleEvidenceAttachmentModule implements RuntimeProcessingModule
}
private String driverKey(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.driverKey(event);
return RuntimeEntityReferenceResolver.driverKey(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.model.UnifiedRuntimeEventBundle;
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 java.time.OffsetDateTime;
import java.util.ArrayList;
@ -156,7 +156,7 @@ public final class DriverWorkingTimeEplEventMapper {
JsonNode attributes = attributes(sourceEvent);
String intervalId = RuntimeEventIdentityResolver.presentationIntervalId(sourceEvent);
String runtimeIntervalKey = RuntimeEventIdentityResolver.runtimeIntervalKey(sourceEvent);
String driverKey = TachographRuntimeIdentityResolver.driverKey(sourceEvent);
String driverKey = RuntimeEntityReferenceResolver.driverKey(sourceEvent);
if (driverKey == null || intervalId == null || runtimeIntervalKey == null) {
return null;
}
@ -175,8 +175,8 @@ public final class DriverWorkingTimeEplEventMapper {
event.put("cardSlot", firstNonBlank(text(raw, "cardSlot"), text(attributes, "cardSlot")));
event.put("cardStatus", firstNonBlank(text(raw, "cardStatus"), text(attributes, "cardStatus")));
event.put("drivingStatus", firstNonBlank(text(raw, "drivingStatus"), text(attributes, "drivingStatus")));
event.put("registrationKey", TachographRuntimeIdentityResolver.registrationKey(sourceEvent));
event.put("vehicleKey", TachographRuntimeIdentityResolver.vehicleKey(sourceEvent));
event.put("registrationKey", RuntimeEntityReferenceResolver.registrationKey(sourceEvent));
event.put("vehicleKey", RuntimeEntityReferenceResolver.vehicleKey(sourceEvent));
event.put("sourceKind", firstNonBlank(text(raw, "sourceKind"), sourceKind(sourceEvent)));
event.put("synthetic", booleanValue(raw, "synthetic", false));
event.put("clippedToRequestedPeriod", booleanValue(raw, "clippedToRequestedPeriod", false));
@ -199,7 +199,7 @@ public final class DriverWorkingTimeEplEventMapper {
JsonNode raw = rawPayload(sourceEvent);
String intervalId = RuntimeEventIdentityResolver.presentationIntervalId(sourceEvent);
String runtimeIntervalKey = RuntimeEventIdentityResolver.runtimeIntervalKey(sourceEvent);
String driverKey = TachographRuntimeIdentityResolver.driverKey(sourceEvent);
String driverKey = RuntimeEntityReferenceResolver.driverKey(sourceEvent);
if (driverKey == null || intervalId == null || runtimeIntervalKey == null) {
return null;
}
@ -214,8 +214,8 @@ public final class DriverWorkingTimeEplEventMapper {
event.put("lifecycle", sourceEvent.lifecycle().name());
event.put("occurredAt", sourceEvent.occurredAt());
event.put("occurredAtEpochSecond", sourceEvent.occurredAt().toEpochSecond());
event.put("registrationKey", TachographRuntimeIdentityResolver.registrationKey(sourceEvent));
event.put("vehicleKey", TachographRuntimeIdentityResolver.vehicleKey(sourceEvent));
event.put("registrationKey", RuntimeEntityReferenceResolver.registrationKey(sourceEvent));
event.put("vehicleKey", RuntimeEntityReferenceResolver.vehicleKey(sourceEvent));
event.put("sourceKind", firstNonBlank(text(raw, "sourceKind"), sourceKind(sourceEvent)));
event.put("odometerKm", odometerKm(sourceEvent, raw));
return event;

View File

@ -388,7 +388,7 @@ public class DriverWorkingTimeRuntimeProcessingPlan implements RuntimeProcessing
sourceSelection.tenantKey(),
sourceSelection.sourceFamilies(),
sourceSelection.eventBackend(),
sourceSelection.tachographSourceKinds(),
sourceSelection.sourceKinds(),
sourceSelection.driverKey(),
driverKeys,
includeAllDrivers,

View File

@ -8,7 +8,7 @@ import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.dto.GeoPointDto;
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.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -82,9 +82,9 @@ public class RuntimeSupportEvidenceNormalizer {
event.eventDomain() == null ? null : event.eventDomain().name(),
event.eventType() == null ? null : event.eventType().name(),
event.lifecycle() == null ? null : event.lifecycle().name(),
firstNonBlank(TachographRuntimeIdentityResolver.driverKey(event), fallbackDriverKey),
TachographRuntimeIdentityResolver.vehicleKey(event),
TachographRuntimeIdentityResolver.registrationKey(event),
firstNonBlank(RuntimeEntityReferenceResolver.driverKey(event), fallbackDriverKey),
RuntimeEntityReferenceResolver.vehicleKey(event),
RuntimeEntityReferenceResolver.registrationKey(event),
event.occurredAt(),
event.occurredAt() == null ? null : event.occurredAt().toEpochSecond(),
latitude,
@ -288,7 +288,7 @@ public class RuntimeSupportEvidenceNormalizer {
}
private JsonNode rawPayload(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.rawPayload(event);
return RuntimeEntityReferenceResolver.rawPayload(event);
}
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,39 @@
package at.procon.eventhub.processing.model;
import at.procon.eventhub.dto.EventHubEventDto;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public record RuntimeTimelineEventBundle(
List<EventHubEventDto> activityEvents,
List<EventHubEventDto> vehicleUsageEvents,
List<EventHubEventDto> supportEvents
) {
public RuntimeTimelineEventBundle {
activityEvents = copy(activityEvents);
vehicleUsageEvents = copy(vehicleUsageEvents);
supportEvents = copy(supportEvents);
}
public static RuntimeTimelineEventBundle empty() {
return new RuntimeTimelineEventBundle(List.of(), List.of(), List.of());
}
public List<EventHubEventDto> allEvents() {
List<EventHubEventDto> result = new ArrayList<>(activityEvents.size() + vehicleUsageEvents.size() + supportEvents.size());
result.addAll(activityEvents);
result.addAll(vehicleUsageEvents);
result.addAll(supportEvents);
result.sort(Comparator.comparing(EventHubEventDto::occurredAt)
.thenComparing(event -> event.eventDomain().name())
.thenComparing(event -> event.eventType().name())
.thenComparing(event -> event.lifecycle().name())
.thenComparing(EventHubEventDto::externalSourceEventId));
return List.copyOf(result);
}
private static List<EventHubEventDto> copy(List<EventHubEventDto> events) {
return events == null ? List.of() : List.copyOf(events);
}
}

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

@ -3,7 +3,6 @@ package at.procon.eventhub.processing.model;
import at.procon.eventhub.reference.DriverCardNumberNormalizer;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
@ -17,7 +16,7 @@ public record UnifiedRuntimeProcessingRequest(
String tenantKey,
Set<UnifiedEventSourceFamily> sourceFamilies,
UnifiedRuntimeEventBackend eventBackend,
Set<UnifiedTachographSourceKind> tachographSourceKinds,
Set<String> sourceKinds,
String driverKey,
Set<String> driverKeys,
boolean includeAllDrivers,
@ -40,7 +39,7 @@ public record UnifiedRuntimeProcessingRequest(
String tenantKey,
Set<UnifiedEventSourceFamily> sourceFamilies,
UnifiedRuntimeEventBackend eventBackend,
Set<UnifiedTachographSourceKind> tachographSourceKinds,
Set<String> sourceKinds,
String driverKey,
Set<String> driverKeys,
boolean includeAllDrivers,
@ -62,7 +61,7 @@ public record UnifiedRuntimeProcessingRequest(
tenantKey,
sourceFamilies,
eventBackend,
tachographSourceKinds,
sourceKinds,
driverKey,
driverKeys,
includeAllDrivers,
@ -87,7 +86,7 @@ public record UnifiedRuntimeProcessingRequest(
throw new IllegalArgumentException("sourceFamilies must not be empty");
}
eventBackend = eventBackend == null ? UnifiedRuntimeEventBackend.SOURCE_DB : eventBackend;
tachographSourceKinds = normalizeTachographSourceKinds(tachographSourceKinds);
sourceKinds = normalizeSourceKinds(sourceKinds);
sessionIds = normalizeSessionIds(sessionId, sessionIds);
if (sessionId == null && !sessionIds.isEmpty()) {
sessionId = sessionIds.get(0);
@ -448,7 +447,7 @@ public record UnifiedRuntimeProcessingRequest(
tenantKey,
Set.of(sourceInput.sourceFamily()),
sourceInput.eventBackend(),
tachographSourceKinds,
sourceKinds,
driverKey,
driverKeys,
includeAllDrivers,
@ -485,7 +484,7 @@ public record UnifiedRuntimeProcessingRequest(
tenantKey,
sourceFamilies,
eventBackend,
tachographSourceKinds,
sourceKinds,
value,
Set.of(),
false,
@ -507,21 +506,15 @@ public record UnifiedRuntimeProcessingRequest(
return includeAllDrivers || driverKeys.size() > 1 || (driverKey == null && !driverKeys.isEmpty());
}
public boolean includesTachographSourceKind(String sourceKind) {
public boolean includesSourceKind(String sourceKind) {
if (sourceKind == null || sourceKind.isBlank()) {
return true;
}
try {
return tachographSourceKinds.contains(UnifiedTachographSourceKind.valueOf(sourceKind.trim().toUpperCase()));
} catch (IllegalArgumentException ex) {
return false;
}
return sourceKinds.contains(sourceKind.trim().toUpperCase());
}
public List<String> tachographSourceKindNames() {
return tachographSourceKinds.stream()
.map(UnifiedTachographSourceKind::name)
.toList();
public List<String> sourceKindNames() {
return sourceKinds.stream().toList();
}
private static Set<UnifiedEventSourceFamily> normalizeSourceFamilies(
@ -600,13 +593,20 @@ public record UnifiedRuntimeProcessingRequest(
return Set.copyOf(normalized);
}
private static Set<UnifiedTachographSourceKind> normalizeTachographSourceKinds(
Set<UnifiedTachographSourceKind> values
private static Set<String> normalizeSourceKinds(
Set<String> values
) {
if (values == null || values.isEmpty()) {
return Set.copyOf(Arrays.asList(UnifiedTachographSourceKind.values()));
return Set.of("DRIVER_CARD", "VEHICLE_UNIT");
}
return Set.copyOf(values);
LinkedHashSet<String> normalized = new LinkedHashSet<>();
for (String value : values) {
String normalizedValue = normalizeUpper(value);
if (normalizedValue != null) {
normalized.add(normalizedValue);
}
}
return normalized.isEmpty() ? Set.of("DRIVER_CARD", "VEHICLE_UNIT") : Set.copyOf(normalized);
}
private static boolean isExternalDbFamily(UnifiedEventSourceFamily family) {

View File

@ -1,6 +0,0 @@
package at.procon.eventhub.processing.model;
public enum UnifiedTachographSourceKind {
DRIVER_CARD,
VEHICLE_UNIT
}

View File

@ -71,7 +71,7 @@ public class EventHubRuntimeEventLoader implements RuntimeDriverEventLoader, Run
request.driverCardNumber(),
request.occurredFrom(),
request.occurredTo(),
request.tachographSourceKindNames(),
request.sourceKindNames(),
request.includeIntersectingIntervals()
);
case YELLOWFOX_DB -> UnifiedDriverEventsRequest.forYellowFoxDbDriver(
@ -100,7 +100,7 @@ public class EventHubRuntimeEventLoader implements RuntimeDriverEventLoader, Run
vehicleRef.registrationNumber(),
request.vehicleOccurredFrom(),
request.vehicleOccurredTo(),
request.tachographSourceKindNames(),
request.sourceKindNames(),
request.includeIntersectingIntervals()
);
case YELLOWFOX_DB -> UnifiedVehicleEventsRequest.forYellowFoxDb(

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

View File

@ -3,9 +3,8 @@ package at.procon.eventhub.processing.service;
import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.processing.model.RuntimeTimelineEventBundle;
import at.procon.eventhub.processing.support.RuntimeEventIdentityResolver;
import com.fasterxml.jackson.databind.JsonNode;
import at.procon.eventhub.tachographfilesession.model.TachographTimelineEventBundle;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@ -16,23 +15,23 @@ final class RuntimeIntervalEventWindowSelector {
private RuntimeIntervalEventWindowSelector() {
}
static TachographTimelineEventBundle filterBundle(
TachographTimelineEventBundle bundle,
static RuntimeTimelineEventBundle filterBundle(
RuntimeTimelineEventBundle bundle,
OffsetDateTime occurredFrom,
OffsetDateTime occurredTo,
boolean includeIntersectingIntervals
) {
if (bundle == null) {
return new TachographTimelineEventBundle(List.of(), List.of(), List.of());
return RuntimeTimelineEventBundle.empty();
}
return new TachographTimelineEventBundle(
return new RuntimeTimelineEventBundle(
filterIntervalEvents(bundle.activityEvents(), occurredFrom, occurredTo, includeIntersectingIntervals),
filterIntervalEvents(bundle.vehicleUsageEvents(), occurredFrom, occurredTo, includeIntersectingIntervals),
filterPointEvents(bundle.supportEvents(), occurredFrom, occurredTo)
);
}
private static List<EventHubEventDto> filterIntervalEvents(
static List<EventHubEventDto> filterIntervalEvents(
List<EventHubEventDto> events,
OffsetDateTime occurredFrom,
OffsetDateTime occurredTo,
@ -59,11 +58,14 @@ final class RuntimeIntervalEventWindowSelector {
return List.copyOf(result);
}
private static List<EventHubEventDto> filterPointEvents(
static List<EventHubEventDto> filterPointEvents(
List<EventHubEventDto> events,
OffsetDateTime occurredFrom,
OffsetDateTime occurredTo
) {
if (events == null || events.isEmpty()) {
return List.of();
}
return events.stream()
.filter(event -> withinWindow(event.occurredAt(), occurredFrom, occurredTo))
.toList();
@ -87,27 +89,6 @@ final class RuntimeIntervalEventWindowSelector {
return RuntimeEventIdentityResolver.runtimeIntervalKey(event);
}
private static JsonNode raw(EventHubEventDto event) {
JsonNode payload = event == null ? null : event.payload();
if (payload == null || payload.isNull() || payload.isMissingNode()) {
return null;
}
JsonNode raw = payload.get("raw");
return raw == null || raw.isNull() ? payload : raw;
}
private static String text(JsonNode node, String field) {
if (node == null || field == null) {
return null;
}
JsonNode value = node.get(field);
if (value == null || value.isNull()) {
return null;
}
String text = value.asText(null);
return text == null || text.isBlank() ? null : text.trim();
}
private static final class IntervalGroup {
private final List<EventHubEventDto> events = new ArrayList<>();
private OffsetDateTime startedAt;

View File

@ -121,7 +121,7 @@ public class TachographDbRuntimeEventLoader implements RuntimeDriverEventLoader,
) {
return definitionRegistry.definitions().stream()
.filter(definition -> !"VEHICLE".equals(definition.entityAxis()))
.filter(definition -> request.includesTachographSourceKind(definition.sourceKind()))
.filter(definition -> request.includesSourceKind(definition.sourceKind()))
.toList();
}
@ -130,7 +130,7 @@ public class TachographDbRuntimeEventLoader implements RuntimeDriverEventLoader,
) {
return definitionRegistry.definitions().stream()
.filter(definition -> !"DRIVER".equals(definition.entityAxis()))
.filter(definition -> request.includesTachographSourceKind(definition.sourceKind()))
.filter(definition -> request.includesSourceKind(definition.sourceKind()))
.toList();
}

View File

@ -54,11 +54,14 @@ public class TachographFileSessionUnifiedDriverEventSource implements UnifiedDri
DriverExtractionSession driver,
UnifiedDriverEventsRequest request
) {
return RuntimeIntervalEventWindowSelector.filterBundle(
eventBuilder.buildEventBundle(session, driver),
TachographTimelineEventBundle bundle = eventBuilder.buildEventBundle(session, driver);
return TachographTimelineEventBundle.fromRuntimeBundle(
RuntimeIntervalEventWindowSelector.filterBundle(
bundle == null ? null : bundle.toRuntimeBundle(),
request.occurredFrom(),
request.occurredTo(),
request.includeIntersectingIntervals()
)
);
}
}

View File

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

View File

@ -5,6 +5,7 @@ import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.processing.model.UnifiedVehicleEventsRequest;
import at.procon.eventhub.reference.TachographNationRegistry;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographTimelineEventBundle;
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
@ -37,18 +38,29 @@ public class TachographFileSessionUnifiedVehicleEventSource implements UnifiedVe
TachographFileSession session = repository.find(request.sessionId())
.orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
return session.driversByKey().values().stream()
.map(driver -> RuntimeIntervalEventWindowSelector.filterBundle(
eventBuilder.buildEventBundle(session, driver),
request.occurredFrom(),
request.occurredTo(),
request.includeIntersectingIntervals()
).allEvents())
.map(driver -> filterBundle(session, driver, request).allEvents())
.flatMap(List::stream)
.filter(event -> matchesVehicle(event.vehicleRef(), request))
.distinct()
.toList();
}
private TachographTimelineEventBundle filterBundle(
TachographFileSession session,
DriverExtractionSession driver,
UnifiedVehicleEventsRequest request
) {
TachographTimelineEventBundle bundle = eventBuilder.buildEventBundle(session, driver);
return TachographTimelineEventBundle.fromRuntimeBundle(
RuntimeIntervalEventWindowSelector.filterBundle(
bundle == null ? null : bundle.toRuntimeBundle(),
request.occurredFrom(),
request.occurredTo(),
request.includeIntersectingIntervals()
)
);
}
private boolean matchesVehicle(VehicleRefDto vehicleRef, UnifiedVehicleEventsRequest request) {
if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
return false;

View File

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

View File

@ -1,11 +1,11 @@
package at.procon.eventhub.processing.service;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
public interface UnifiedDriverTimelineSource {
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.EventLifecycle;
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.TachographRuntimeIdentityResolver;
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 at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import com.fasterxml.jackson.databind.JsonNode;
import java.math.BigDecimal;
import java.time.Duration;
@ -27,7 +27,7 @@ import org.springframework.stereotype.Service;
@Service
public class UnifiedEventTimelineReconstructor {
public ResolvedDriverTimeline reconstruct(
public RuntimeDriverTimeline reconstruct(
UUID sessionId,
String driverKey,
List<EventHubEventDto> events
@ -35,23 +35,23 @@ public class UnifiedEventTimelineReconstructor {
return reconstruct(sessionId, driverKey, events, List.of(), null);
}
public ResolvedDriverTimeline reconstruct(
public RuntimeDriverTimeline reconstruct(
UUID sessionId,
String driverKey,
List<EventHubEventDto> events,
List<ExtractionWarning> warnings,
List<RuntimeExtractionWarning> warnings,
String sourceKind
) {
List<EventHubEventDto> safeEvents = events == null ? List.of() : List.copyOf(events);
List<ResolvedActivityInterval> activityIntervals = reconstructActivityIntervals(safeEvents);
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals =
List<RuntimeActivityInterval> activityIntervals = reconstructActivityIntervals(safeEvents);
List<RuntimeVehicleUsageInterval> vehicleUsageIntervals =
reconstructVehicleUsageIntervals(sessionId, driverKey, safeEvents);
List<ExtractedSupportEvent> supportEvents = reconstructSupportEvents(safeEvents);
List<ExtractionWarning> mergedWarnings = warnings == null ? List.of() : List.copyOf(new LinkedHashSet<>(warnings));
List<RuntimeSupportEvent> supportEvents = reconstructSupportEvents(safeEvents);
List<RuntimeExtractionWarning> mergedWarnings = warnings == null ? List.of() : List.copyOf(new LinkedHashSet<>(warnings));
OffsetDateTime loadedFrom = minTimestamp(activityIntervals, vehicleUsageIntervals, supportEvents);
OffsetDateTime loadedTo = maxTimestamp(activityIntervals, vehicleUsageIntervals, supportEvents);
return new ResolvedDriverTimeline(
return new RuntimeDriverTimeline(
resolveSourceKind(safeEvents, sourceKind),
loadedFrom,
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<>();
for (EventHubEventDto event : events) {
if (event.eventDomain() != EventDomain.DRIVER_ACTIVITY) {
@ -83,13 +83,13 @@ public class UnifiedEventTimelineReconstructor {
return byIntervalId.values().stream()
.map(ActivityAccumulator::finish)
.filter(interval -> interval != null)
.sorted(Comparator.comparing(ResolvedActivityInterval::from)
.thenComparing(ResolvedActivityInterval::to)
.thenComparing(ResolvedActivityInterval::activityType, Comparator.nullsLast(String::compareTo)))
.sorted(Comparator.comparing(RuntimeActivityInterval::from)
.thenComparing(RuntimeActivityInterval::to)
.thenComparing(RuntimeActivityInterval::activityType, Comparator.nullsLast(String::compareTo)))
.toList();
}
private List<ResolvedVehicleUsageInterval> reconstructVehicleUsageIntervals(
private List<RuntimeVehicleUsageInterval> reconstructVehicleUsageIntervals(
UUID sessionId,
String driverKey,
List<EventHubEventDto> events
@ -117,13 +117,13 @@ public class UnifiedEventTimelineReconstructor {
return byIntervalId.values().stream()
.map(VehicleUsageAccumulator::finish)
.filter(interval -> interval != null)
.sorted(Comparator.comparing(ResolvedVehicleUsageInterval::from)
.thenComparing(ResolvedVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder())))
.sorted(Comparator.comparing(RuntimeVehicleUsageInterval::from)
.thenComparing(RuntimeVehicleUsageInterval::to, Comparator.nullsLast(Comparator.naturalOrder())))
.toList();
}
private List<ExtractedSupportEvent> reconstructSupportEvents(List<EventHubEventDto> events) {
List<ExtractedSupportEvent> result = new ArrayList<>();
private List<RuntimeSupportEvent> reconstructSupportEvents(List<EventHubEventDto> events) {
List<RuntimeSupportEvent> result = new ArrayList<>();
for (EventHubEventDto event : events) {
if (event.eventDomain() == EventDomain.DRIVER_ACTIVITY) {
continue;
@ -136,16 +136,16 @@ public class UnifiedEventTimelineReconstructor {
String eventId = firstNonBlank(text(raw, "supportEventId"), event.externalSourceEventId());
BigDecimal latitude = event.position() == null ? null : event.position().latitude();
BigDecimal longitude = event.position() == null ? null : event.position().longitude();
result.add(new ExtractedSupportEvent(
result.add(new RuntimeSupportEvent(
eventId,
TachographRuntimeIdentityResolver.driverKey(event),
RuntimeEntityReferenceResolver.driverKey(event),
event.occurredAt(),
event.eventDomain().name(),
text(raw, "supportEventType") == null ? event.eventType().name() : text(raw, "supportEventType"),
event.lifecycle().name(),
text(raw, "slot"),
TachographRuntimeIdentityResolver.registrationKey(event),
TachographRuntimeIdentityResolver.vehicleKey(event),
RuntimeEntityReferenceResolver.registrationKey(event),
RuntimeEntityReferenceResolver.vehicleKey(event),
firstNonBlank(text(raw, "country"), detailText(event, "country")),
text(raw, "region"),
firstNonBlank(text(raw, "countryFrom"), detailText(event, "countryFrom")),
@ -161,9 +161,9 @@ public class UnifiedEventTimelineReconstructor {
text(raw, "rawRecordPath")
));
}
result.sort(Comparator.comparing(ExtractedSupportEvent::occurredAt)
.thenComparing(ExtractedSupportEvent::eventDomain, Comparator.nullsLast(String::compareTo))
.thenComparing(ExtractedSupportEvent::eventId, Comparator.nullsLast(String::compareTo)));
result.sort(Comparator.comparing(RuntimeSupportEvent::occurredAt)
.thenComparing(RuntimeSupportEvent::eventDomain, Comparator.nullsLast(String::compareTo))
.thenComparing(RuntimeSupportEvent::eventId, Comparator.nullsLast(String::compareTo)));
return List.copyOf(result);
}
@ -189,36 +189,36 @@ public class UnifiedEventTimelineReconstructor {
}
private OffsetDateTime minTimestamp(
List<ResolvedActivityInterval> activityIntervals,
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
List<ExtractedSupportEvent> supportEvents
List<RuntimeActivityInterval> activityIntervals,
List<RuntimeVehicleUsageInterval> vehicleUsageIntervals,
List<RuntimeSupportEvent> supportEvents
) {
OffsetDateTime min = null;
for (ResolvedActivityInterval interval : activityIntervals) {
for (RuntimeActivityInterval interval : activityIntervals) {
min = min(min, interval.from());
}
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
for (RuntimeVehicleUsageInterval interval : vehicleUsageIntervals) {
min = min(min, interval.from());
}
for (ExtractedSupportEvent supportEvent : supportEvents) {
for (RuntimeSupportEvent supportEvent : supportEvents) {
min = min(min, supportEvent.occurredAt());
}
return min;
}
private OffsetDateTime maxTimestamp(
List<ResolvedActivityInterval> activityIntervals,
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
List<ExtractedSupportEvent> supportEvents
List<RuntimeActivityInterval> activityIntervals,
List<RuntimeVehicleUsageInterval> vehicleUsageIntervals,
List<RuntimeSupportEvent> supportEvents
) {
OffsetDateTime max = null;
for (ResolvedActivityInterval interval : activityIntervals) {
for (RuntimeActivityInterval interval : activityIntervals) {
max = max(max, interval.to());
}
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
for (RuntimeVehicleUsageInterval interval : vehicleUsageIntervals) {
max = max(max, interval.to());
}
for (ExtractedSupportEvent supportEvent : supportEvents) {
for (RuntimeSupportEvent supportEvent : supportEvents) {
max = max(max, supportEvent.occurredAt());
}
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)) {
return null;
}
return new ResolvedActivityInterval(
return new RuntimeActivityInterval(
intervalId,
startedAt,
endedAt,
@ -383,8 +383,8 @@ public class UnifiedEventTimelineReconstructor {
detailText(sample, "cardSlot"),
detailText(sample, "cardStatus"),
detailText(sample, "drivingStatus"),
TachographRuntimeIdentityResolver.registrationKey(sample),
TachographRuntimeIdentityResolver.vehicleKey(sample),
RuntimeEntityReferenceResolver.registrationKey(sample),
RuntimeEntityReferenceResolver.vehicleKey(sample),
text(raw, "sourceKind"),
stringList(raw, "sourceRowIds"),
booleanValue(raw, "synthetic"),
@ -427,11 +427,11 @@ public class UnifiedEventTimelineReconstructor {
}
}
private ResolvedVehicleUsageInterval finish() {
private RuntimeVehicleUsageInterval finish() {
if (startedAt == null) {
return null;
}
return ResolvedVehicleUsageInterval.resolved(
return RuntimeVehicleUsageInterval.resolved(
sessionId,
driverKey,
intervalId,
@ -439,8 +439,8 @@ public class UnifiedEventTimelineReconstructor {
endedAt,
odometerBeginKm,
odometerEndKm,
sample == null ? null : TachographRuntimeIdentityResolver.registrationKey(sample),
sample == null ? null : TachographRuntimeIdentityResolver.vehicleKey(sample),
sample == null ? null : RuntimeEntityReferenceResolver.registrationKey(sample),
sample == null ? null : RuntimeEntityReferenceResolver.vehicleKey(sample),
text(raw, "sourceKind"),
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.DriverWorkingTimeReusableProjectionBuilder;
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.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.RuntimeSupportEvent;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
import at.procon.eventhub.tachographfilesession.service.TachographDriverWorkingTimeAdapter;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.processing.support.RuntimeDriverWorkingTimeAdapter;
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
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.TachographEsperVehicleUsageIntervalEvent;
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.OffsetDateTime;
import java.time.ZoneOffset;
@ -107,7 +106,7 @@ public class UnifiedRuntimeDerivedProjectionService {
eventBundle.mergedEvents()
);
List<EventHubEventDto> normalizedEvents = normalizationResult.normalizedEvents();
ResolvedDriverTimeline timeline = timelineReconstructor.reconstruct(
RuntimeDriverTimeline timeline = timelineReconstructor.reconstruct(
runtimeSessionId(request),
driverKey,
normalizedEvents
@ -150,7 +149,7 @@ public class UnifiedRuntimeDerivedProjectionService {
significantDrivingMinutes,
minimumRestPeriodMinutes,
timeline.activityIntervals().stream()
.map(interval -> TachographDriverWorkingTimeAdapter.toActivityInterval(
.map(interval -> RuntimeDriverWorkingTimeAdapter.toActivityInterval(
runtimeSessionId(request),
driverKey,
interval
@ -158,11 +157,11 @@ public class UnifiedRuntimeDerivedProjectionService {
.filter(Objects::nonNull)
.toList(),
timeline.vehicleUsageIntervals().stream()
.map(TachographDriverWorkingTimeAdapter::toVehicleUsageInterval)
.map(RuntimeDriverWorkingTimeAdapter::toVehicleUsageInterval)
.filter(Objects::nonNull)
.toList(),
timeline.supportEvents().stream()
.map(this::toSupportEvidenceEvent)
.map(RuntimeDriverWorkingTimeAdapter::toSupportEvidenceEvent)
.filter(Objects::nonNull)
.toList(),
notes
@ -207,41 +206,11 @@ public class UnifiedRuntimeDerivedProjectionService {
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(
UnifiedRuntimeProcessingRequest request,
List<EventHubEventDto> events
) {
return TachographRuntimeIdentityResolver.requestDriverKey(request, events);
return RuntimeEntityReferenceResolver.requestDriverKey(request, events);
}
@ -436,12 +405,12 @@ public class UnifiedRuntimeDerivedProjectionService {
}
private List<TachographEsperSupportGeoEvent> clipSupportGeoEvents(
List<ExtractedSupportEvent> supportEvents,
List<RuntimeSupportEvent> supportEvents,
String driverKey,
OffsetDateTime requestedFrom,
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 -> driverKey == null || event.driverKey() == null || Objects.equals(driverKey, event.driverKey()))
.filter(event -> requestedFrom == null || !event.occurredAt().isBefore(requestedFrom))

View File

@ -1,10 +1,10 @@
package at.procon.eventhub.processing.service;
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.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.processing.support.TachographRuntimeIdentityResolver;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import at.procon.eventhub.processing.support.RuntimeEntityReferenceResolver;
import java.util.List;
import org.springframework.stereotype.Service;
@ -22,7 +22,7 @@ public class UnifiedRuntimeDriverTimelineService {
this.timelineReconstructor = timelineReconstructor;
}
public ResolvedDriverTimeline loadDriverTimeline(UnifiedRuntimeProcessingRequest request) {
public RuntimeDriverTimeline loadDriverTimeline(UnifiedRuntimeProcessingRequest request) {
UnifiedRuntimeEventBundle bundle = runtimeEventAssemblyService.assembleDriverScopedEvents(request);
return timelineReconstructor.reconstruct(
null,
@ -35,6 +35,6 @@ public class UnifiedRuntimeDriverTimelineService {
UnifiedRuntimeProcessingRequest request,
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 java.util.List;
public final class TachographRuntimeIdentityResolver {
public final class RuntimeEntityReferenceResolver {
private TachographRuntimeIdentityResolver() {
private RuntimeEntityReferenceResolver() {
}
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.lifecycle() == null ? null : event.lifecycle().name()));
parts.add(normalizeTime(event.occurredAt()));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.driverKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.registrationKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.vehicleKey(event)));
parts.add(nullToEmpty(RuntimeEntityReferenceResolver.driverKey(event)));
parts.add(nullToEmpty(RuntimeEntityReferenceResolver.registrationKey(event)));
parts.add(nullToEmpty(RuntimeEntityReferenceResolver.vehicleKey(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, "slot"), text(raw, "cardSlot"), text(details, "cardSlot"))));
@ -72,9 +72,9 @@ public final class RuntimeEventIdentityResolver {
parts.add("INTERVAL");
parts.add(nullToEmpty(event.eventDomain() == null ? null : event.eventDomain().name()));
parts.add(nullToEmpty(intervalSemanticType(event, raw)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.driverKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.registrationKey(event)));
parts.add(nullToEmpty(TachographRuntimeIdentityResolver.vehicleKey(event)));
parts.add(nullToEmpty(RuntimeEntityReferenceResolver.driverKey(event)));
parts.add(nullToEmpty(RuntimeEntityReferenceResolver.registrationKey(event)));
parts.add(nullToEmpty(RuntimeEntityReferenceResolver.vehicleKey(event)));
parts.add(nullToEmpty(intervalCardSlot(event, raw, details)));
parts.add(nullToEmpty(intervalCardStatus(event, raw, details)));
parts.add(nullToEmpty(intervalDrivingStatus(event, raw, details)));
@ -152,7 +152,7 @@ public final class RuntimeEventIdentityResolver {
}
private static JsonNode rawPayload(EventHubEventDto event) {
return TachographRuntimeIdentityResolver.rawPayload(event);
return RuntimeEntityReferenceResolver.rawPayload(event);
}
private static JsonNode detailAttributes(EventHubEventDto event) {

View File

@ -1,6 +1,7 @@
package at.procon.eventhub.tachographfilesession.model;
import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.processing.model.RuntimeTimelineEventBundle;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@ -29,6 +30,16 @@ public record TachographTimelineEventBundle(
return List.copyOf(result);
}
public RuntimeTimelineEventBundle toRuntimeBundle() {
return new RuntimeTimelineEventBundle(activityEvents, vehicleUsageEvents, supportEvents);
}
public static TachographTimelineEventBundle fromRuntimeBundle(RuntimeTimelineEventBundle bundle) {
return bundle == null
? new TachographTimelineEventBundle(List.of(), List.of(), List.of())
: new TachographTimelineEventBundle(bundle.activityEvents(), bundle.vehicleUsageEvents(), bundle.supportEvents());
}
private static List<EventHubEventDto> copy(List<EventHubEventDto> events) {
return events == null ? List.of() : List.copyOf(events);
}

View File

@ -1,5 +1,6 @@
package at.procon.eventhub.tachographfilesession.service;
import at.procon.eventhub.tachographfilesession.support.RuntimeTimelineCompatibilityAdapter;
import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.ExtractionWarning;
@ -35,12 +36,16 @@ public class EventBackedDriverTimelineBuilder {
DriverExtractionSession driverSession
) {
TachographTimelineEventBundle bundle = eventBuilder.buildEventBundle(session, driverSession);
return timelineReconstructor.reconstruct(
return RuntimeTimelineCompatibilityAdapter.toResolvedDriverTimeline(
timelineReconstructor.reconstruct(
session.sessionId(),
driverSession.driverKey(),
bundle.allEvents(),
mergeWarnings(session.warnings(), driverSession.warnings()),
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.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.tachographfilesession.support.RuntimeTimelineCompatibilityAdapter;
import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
import at.procon.eventhub.service.EventHubEventSorter;
import at.procon.eventhub.tachographfilesession.dto.CreateTachographCompositeSessionRequest;
@ -119,12 +120,16 @@ public class TachographCompositeSessionService {
throw new DriverNotFoundInCompositeSessionException(compositeSessionId, driverKey);
}
List<EventHubEventDto> mergedEvents = mergeDriverEvents(sourceSessions, driverKey);
return timelineReconstructor.reconstruct(
return RuntimeTimelineCompatibilityAdapter.toResolvedDriverTimeline(
timelineReconstructor.reconstruct(
compositeSessionId,
driverKey,
mergedEvents,
mergeWarnings(sourceSessions, driverKey),
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 at.procon.eventhub.dto.EventHubEventDto;
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.ResolvedVehicleUsageInterval;
import java.time.OffsetDateTime;
@ -51,10 +52,12 @@ public class TachographEventTimelineReconstructionHelper {
String fallbackDriverKey,
List<EventHubEventDto> events
) {
ResolvedDriverTimeline reconstructed = timelineReconstructor.reconstruct(
ResolvedDriverTimeline reconstructed = RuntimeTimelineCompatibilityAdapter.toResolvedDriverTimeline(
timelineReconstructor.reconstruct(
fallbackSessionId == null ? new UUID(0L, 0L) : fallbackSessionId,
fallbackDriverKey,
safe(events)
)
);
return new ResolvedDriverTimeline(
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.UnifiedRuntimeEventBackend;
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.service.UnifiedRuntimeDerivedProjectionService;
import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService;
import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService;
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.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -123,11 +123,11 @@ class UnifiedRuntimeProcessingControllerTest {
.build();
when(timelineService.loadDriverTimeline(any()))
.thenReturn(new ResolvedDriverTimeline(
.thenReturn(new RuntimeDriverTimeline(
"UNIFIED_EVENT_STREAM",
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
List.of(ResolvedVehicleUsageInterval.resolved(
List.of(RuntimeVehicleUsageInterval.resolved(
null,
"DRIVER:42",
"CVU-1",
@ -140,7 +140,7 @@ class UnifiedRuntimeProcessingControllerTest {
"DRIVER_CARD",
List.of("CVU-1")
)),
List.of(new ResolvedActivityInterval(
List.of(new RuntimeActivityInterval(
"ACT-1",
OffsetDateTime.parse("2026-05-01T08:30:00Z"),
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
@ -181,7 +181,6 @@ class UnifiedRuntimeProcessingControllerTest {
.andExpect(jsonPath("$.vehicleUsageIntervals[0].intervalId").value("CVU-1"));
}
@Test
void loadsDriverDerivedProjectionsViaRuntimeApi() throws Exception {
UnifiedRuntimeEventAssemblyService eventAssemblyService = org.mockito.Mockito.mock(UnifiedRuntimeEventAssemblyService.class);

View File

@ -67,12 +67,12 @@ class UnifiedRuntimeProcessingRequestTest {
OffsetDateTime.parse("2026-05-02T00:00:00Z")
);
assertThat(defaultRequest.tachographSourceKinds()).containsExactlyInAnyOrder(
UnifiedTachographSourceKind.DRIVER_CARD,
UnifiedTachographSourceKind.VEHICLE_UNIT
assertThat(defaultRequest.sourceKinds()).containsExactlyInAnyOrder(
"DRIVER_CARD",
"VEHICLE_UNIT"
);
assertThat(defaultRequest.includesTachographSourceKind("DRIVER_CARD")).isTrue();
assertThat(defaultRequest.includesTachographSourceKind("VEHICLE_UNIT")).isTrue();
assertThat(defaultRequest.includesSourceKind("DRIVER_CARD")).isTrue();
assertThat(defaultRequest.includesSourceKind("VEHICLE_UNIT")).isTrue();
UnifiedRuntimeProcessingRequest driverCardOnlyRequest = new UnifiedRuntimeProcessingRequest(
null,
@ -81,7 +81,7 @@ class UnifiedRuntimeProcessingRequestTest {
"default",
Set.of(UnifiedEventSourceFamily.TACHOGRAPH_DB),
UnifiedRuntimeEventBackend.SOURCE_DB,
Set.of(UnifiedTachographSourceKind.DRIVER_CARD),
Set.of("DRIVER_CARD"),
null,
Set.of(),
false,
@ -97,9 +97,9 @@ class UnifiedRuntimeProcessingRequestTest {
true
);
assertThat(driverCardOnlyRequest.tachographSourceKinds()).containsExactly(UnifiedTachographSourceKind.DRIVER_CARD);
assertThat(driverCardOnlyRequest.includesTachographSourceKind("DRIVER_CARD")).isTrue();
assertThat(driverCardOnlyRequest.includesTachographSourceKind("VEHICLE_UNIT")).isFalse();
assertThat(driverCardOnlyRequest.sourceKinds()).containsExactly("DRIVER_CARD");
assertThat(driverCardOnlyRequest.includesSourceKind("DRIVER_CARD")).isTrue();
assertThat(driverCardOnlyRequest.includesSourceKind("VEHICLE_UNIT")).isFalse();
}
@Test

View File

@ -3,6 +3,7 @@ package at.procon.eventhub.processing.service;
import static org.assertj.core.api.Assertions.assertThat;
import at.procon.eventhub.config.EventHubProperties;
import at.procon.eventhub.processing.model.RuntimeDriverTimeline;
import at.procon.eventhub.processing.model.UnifiedDriverTimelineRequest;
import at.procon.eventhub.service.EventDetailsFactory;
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.ExtractedVehicleRegistration;
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.TachographFileSessionMetadata;
import at.procon.eventhub.tachographfilesession.service.DriverKeyFactory;
@ -55,7 +55,7 @@ class UnifiedDriverTimelineServiceTest {
TachographFileSession session = session(driver);
repository.save(session);
ResolvedDriverTimeline timeline = service.loadDriverTimeline(
RuntimeDriverTimeline timeline = service.loadDriverTimeline(
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.VehicleRefDto;
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.ObjectNode;
import java.math.BigDecimal;
@ -38,7 +38,7 @@ class UnifiedEventTimelineReconstructorTest {
supportEvent("SUP-1", "2026-05-01T08:30:00Z")
);
ResolvedDriverTimeline timeline = reconstructor.reconstruct(
RuntimeDriverTimeline timeline = reconstructor.reconstruct(
UUID.randomUUID(),
"DRIVER:42",
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")
);
ResolvedDriverTimeline timeline = reconstructor.reconstruct(
RuntimeDriverTimeline timeline = reconstructor.reconstruct(
UUID.randomUUID(),
"DRIVER:42",
events
@ -105,7 +105,7 @@ class UnifiedEventTimelineReconstructorTest {
)
);
ResolvedDriverTimeline timeline = reconstructor.reconstruct(
RuntimeDriverTimeline timeline = reconstructor.reconstruct(
UUID.randomUUID(),
"DRIVER:42",
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.UnifiedEventSourceFamily;
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.tachographfilesession.model.ResolvedDriverTimeline;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.time.LocalDate;
@ -40,7 +40,7 @@ class UnifiedRuntimeDriverTimelineServiceTest {
new UnifiedEventTimelineReconstructor()
);
ResolvedDriverTimeline timeline = service.loadDriverTimeline(
RuntimeDriverTimeline timeline = service.loadDriverTimeline(
new UnifiedRuntimeProcessingRequest(
null,
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.VehicleRefDto;
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.service.EventAcquisitionRecordKeyService;
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(second, secondDriver)).thenReturn(List.of(event("b", occurredAt.plusHours(1), EventType.DRIVE)));
ResolvedDriverTimeline expected = new ResolvedDriverTimeline(
RuntimeDriverTimeline reconstructed = new RuntimeDriverTimeline(
"COMPOSITE_TACHOGRAPH_FILE_SESSION",
occurredAt,
occurredAt.plusHours(1),
@ -185,14 +187,22 @@ class TachographCompositeSessionServiceTest {
List.of()
);
when(reconstructor.reconstruct(any(), eq("12:123"), any(), any(), eq("COMPOSITE_TACHOGRAPH_FILE_SESSION")))
.thenReturn(expected);
.thenReturn(reconstructed);
ResolvedDriverTimeline actual =
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<ExtractionWarning>> warningsCaptor = ArgumentCaptor.forClass(List.class);
ArgumentCaptor<List<RuntimeExtractionWarning>> warningsCaptor = ArgumentCaptor.forClass(List.class);
verify(reconstructor).reconstruct(
eq(created.session().compositeSessionId()),
eq("12:123"),
@ -203,10 +213,10 @@ class TachographCompositeSessionServiceTest {
assertThat(eventsCaptor.getValue()).extracting(EventHubEventDto::externalSourceEventId)
.containsExactly("a", "b");
assertThat(warningsCaptor.getValue()).containsExactly(
new ExtractionWarning("SESSION_A", "warn-a", "s1"),
new ExtractionWarning("CARD_GAP", "gap", "p1"),
new ExtractionWarning("SESSION_B", "warn-b", "s2"),
new ExtractionWarning("DUPLICATE", "duplicate", "p2")
new RuntimeExtractionWarning("SESSION_A", "warn-a", "s1"),
new RuntimeExtractionWarning("CARD_GAP", "gap", "p1"),
new RuntimeExtractionWarning("SESSION_B", "warn-b", "s2"),
new RuntimeExtractionWarning("DUPLICATE", "duplicate", "p2")
);
}