Extract neutral working-time projection engine
This commit is contained in:
parent
4b5691d20f
commit
cd090010c6
|
|
@ -0,0 +1,31 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record DriverWorkingTimeDerivedProjectionBundle(
|
||||||
|
List<DriverWorkingTimeDrivingInterruptionInterval> drivingInterruptionIntervals,
|
||||||
|
List<DriverWorkingTimeDrivingInterruptionInterval> dailyWeeklyRestCandidateIntervals,
|
||||||
|
List<DriverWorkingTimeRestCoverageInterval> dailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
List<DriverWorkingTimeRestCoverageInterval> unclassifiedDailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
List<DriverWorkingTimeDrivingInterruptionInterval> drivingInterruptionVehicleChangeIntervals,
|
||||||
|
List<DriverWorkingTimeVuCardAbsentInterval> vuCardAbsentIntervals,
|
||||||
|
List<DriverWorkingTimeRestCoverageInterval> potentialHomeOvernightStayIntervals,
|
||||||
|
List<DriverWorkingTimeRestCoverageInterval> potentialInVehicleOvernightStayIntervals,
|
||||||
|
List<DriverWorkingTimePotentialInVehicleTripInterval> potentialInVehicleTripIntervals
|
||||||
|
) {
|
||||||
|
public DriverWorkingTimeDerivedProjectionBundle {
|
||||||
|
drivingInterruptionIntervals = safe(drivingInterruptionIntervals);
|
||||||
|
dailyWeeklyRestCandidateIntervals = safe(dailyWeeklyRestCandidateIntervals);
|
||||||
|
dailyWeeklyRestCandidateCoverageIntervals = safe(dailyWeeklyRestCandidateCoverageIntervals);
|
||||||
|
unclassifiedDailyWeeklyRestCandidateCoverageIntervals = safe(unclassifiedDailyWeeklyRestCandidateCoverageIntervals);
|
||||||
|
drivingInterruptionVehicleChangeIntervals = safe(drivingInterruptionVehicleChangeIntervals);
|
||||||
|
vuCardAbsentIntervals = safe(vuCardAbsentIntervals);
|
||||||
|
potentialHomeOvernightStayIntervals = safe(potentialHomeOvernightStayIntervals);
|
||||||
|
potentialInVehicleOvernightStayIntervals = safe(potentialInVehicleOvernightStayIntervals);
|
||||||
|
potentialInVehicleTripIntervals = safe(potentialInVehicleTripIntervals);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> safe(List<T> values) {
|
||||||
|
return values == null ? List.of() : List.copyOf(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record DriverWorkingTimeDrivingInterruptionInterval(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime startedAt,
|
||||||
|
OffsetDateTime endedAt,
|
||||||
|
long durationSeconds,
|
||||||
|
String previousDrivingSourceIntervalId,
|
||||||
|
String nextDrivingSourceIntervalId,
|
||||||
|
String previousRegistrationKey,
|
||||||
|
String nextRegistrationKey,
|
||||||
|
String previousVehicleKey,
|
||||||
|
String nextVehicleKey
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
public record DriverWorkingTimeGeoEvidenceEvent(
|
||||||
|
String eventId,
|
||||||
|
String eventDomain,
|
||||||
|
OffsetDateTime occurredAt,
|
||||||
|
Double latitude,
|
||||||
|
Double longitude,
|
||||||
|
Long distanceSeconds,
|
||||||
|
Long odometerKm
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record DriverWorkingTimePotentialInVehicleTripInterval(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime startedAt,
|
||||||
|
OffsetDateTime endedAt,
|
||||||
|
long durationSeconds,
|
||||||
|
String registrationKey,
|
||||||
|
String vehicleKey,
|
||||||
|
int containedPotentialInVehicleOvernightStayIntervalCount,
|
||||||
|
long containedPotentialInVehicleOvernightStayDurationSeconds,
|
||||||
|
long containedCardAbsentDurationSeconds,
|
||||||
|
OffsetDateTime firstPotentialInVehicleOvernightStayStartedAt,
|
||||||
|
OffsetDateTime lastPotentialInVehicleOvernightStayEndedAt,
|
||||||
|
String firstPreviousDrivingSourceIntervalId,
|
||||||
|
String lastNextDrivingSourceIntervalId,
|
||||||
|
List<DriverWorkingTimeRestCoverageInterval> potentialInVehicleOvernightStayIntervals
|
||||||
|
) {
|
||||||
|
public DriverWorkingTimePotentialInVehicleTripInterval {
|
||||||
|
potentialInVehicleOvernightStayIntervals = potentialInVehicleOvernightStayIntervals == null
|
||||||
|
? List.of()
|
||||||
|
: List.copyOf(potentialInVehicleOvernightStayIntervals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record DriverWorkingTimeRestCoverageInterval(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime startedAt,
|
||||||
|
OffsetDateTime endedAt,
|
||||||
|
long durationSeconds,
|
||||||
|
long cardAbsentDurationSeconds,
|
||||||
|
double cardAbsentCoveragePercent,
|
||||||
|
String previousDrivingSourceIntervalId,
|
||||||
|
String nextDrivingSourceIntervalId,
|
||||||
|
String previousRegistrationKey,
|
||||||
|
String nextRegistrationKey,
|
||||||
|
String previousVehicleKey,
|
||||||
|
String nextVehicleKey,
|
||||||
|
Long beginBoundaryOdometerKm,
|
||||||
|
Long endBoundaryOdometerKm,
|
||||||
|
String beginGeoEventId,
|
||||||
|
String beginGeoEventDomain,
|
||||||
|
OffsetDateTime beginGeoOccurredAt,
|
||||||
|
Double beginLatitude,
|
||||||
|
Double beginLongitude,
|
||||||
|
Long beginGeoDistanceSeconds,
|
||||||
|
Long beginGeoOdometerKm,
|
||||||
|
String endGeoEventId,
|
||||||
|
String endGeoEventDomain,
|
||||||
|
OffsetDateTime endGeoOccurredAt,
|
||||||
|
Double endLatitude,
|
||||||
|
Double endLongitude,
|
||||||
|
Long endGeoDistanceSeconds,
|
||||||
|
Long endGeoOdometerKm,
|
||||||
|
Long geoEvidenceMovementMeters,
|
||||||
|
String geoEvidenceMovementCategory,
|
||||||
|
DriverWorkingTimeGeoEvidenceEvent beginGeoEvent,
|
||||||
|
DriverWorkingTimeGeoEvidenceEvent endGeoEvent
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
public record DriverWorkingTimeSupportGeoEvent(
|
||||||
|
String eventId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime occurredAt,
|
||||||
|
String eventDomain,
|
||||||
|
String eventType,
|
||||||
|
String eventLifecycle,
|
||||||
|
String registrationKey,
|
||||||
|
String vehicleKey,
|
||||||
|
String country,
|
||||||
|
String region,
|
||||||
|
String countryFrom,
|
||||||
|
String countryTo,
|
||||||
|
String operation,
|
||||||
|
BigDecimal latitude,
|
||||||
|
BigDecimal longitude,
|
||||||
|
Long odometerKm,
|
||||||
|
String rawRecordPath
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record DriverWorkingTimeVuCardAbsentInterval(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime startedAt,
|
||||||
|
OffsetDateTime endedAt,
|
||||||
|
long durationSeconds,
|
||||||
|
String previousUsageIntervalId,
|
||||||
|
String nextUsageIntervalId,
|
||||||
|
String previousRegistrationKey,
|
||||||
|
String nextRegistrationKey,
|
||||||
|
String previousVehicleKey,
|
||||||
|
String nextVehicleKey
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,426 @@
|
||||||
|
package at.procon.eventhub.processing.driverworkingtime.service;
|
||||||
|
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeDerivedProjectionBundle;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeDrivingInterruptionInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeGeoEvidenceEvent;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimePotentialInVehicleTripInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeRestCoverageInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVuCardAbsentInterval;
|
||||||
|
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperGeoEvidenceEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleTripIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.service.DriverTimelineReusableProjectionBuilder;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DriverWorkingTimeDerivedProjectionEngine {
|
||||||
|
|
||||||
|
private final DriverTimelineReusableProjectionBuilder legacyProjectionBuilder;
|
||||||
|
|
||||||
|
public DriverWorkingTimeDerivedProjectionEngine(
|
||||||
|
DriverTimelineReusableProjectionBuilder legacyProjectionBuilder
|
||||||
|
) {
|
||||||
|
this.legacyProjectionBuilder = legacyProjectionBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DriverWorkingTimeDerivedProjectionBundle build(DriverWorkingTimeProcessingInput input) {
|
||||||
|
Objects.requireNonNull(input, "input must not be null");
|
||||||
|
ResolvedDriverTimeline timeline = toResolvedTimeline(input);
|
||||||
|
TachographEsperDrivingDerivedProjectionBundle legacyBundle =
|
||||||
|
legacyProjectionBuilder.buildEsperDrivingDerivedProjectionBundle(
|
||||||
|
safeSessionId(input.sessionId()),
|
||||||
|
input.driverKey(),
|
||||||
|
timeline,
|
||||||
|
input.significantDrivingMinutes(),
|
||||||
|
input.minimumRestPeriodMinutes()
|
||||||
|
);
|
||||||
|
return new DriverWorkingTimeDerivedProjectionBundle(
|
||||||
|
legacyBundle.drivingInterruptionIntervals().stream().map(this::mapDrivingInterruption).toList(),
|
||||||
|
legacyBundle.dailyWeeklyRestCandidateIntervals().stream().map(this::mapDrivingInterruption).toList(),
|
||||||
|
legacyBundle.dailyWeeklyRestCandidateCoverageIntervals().stream().map(this::mapRestCoverage).toList(),
|
||||||
|
legacyBundle.unclassifiedDailyWeeklyRestCandidateCoverageIntervals().stream().map(this::mapRestCoverage).toList(),
|
||||||
|
legacyBundle.drivingInterruptionVehicleChangeIntervals().stream().map(this::mapDrivingInterruption).toList(),
|
||||||
|
legacyBundle.vuCardAbsentIntervals().stream().map(this::mapVuCardAbsent).toList(),
|
||||||
|
legacyBundle.potentialHomeOvernightStayIntervals().stream().map(this::mapPotentialHome).toList(),
|
||||||
|
legacyBundle.potentialInVehicleOvernightStayIntervals().stream().map(this::mapPotentialInVehicle).toList(),
|
||||||
|
legacyBundle.potentialInVehicleTripIntervals().stream().map(this::mapPotentialTrip).toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResolvedDriverTimeline toResolvedTimeline(DriverWorkingTimeProcessingInput input) {
|
||||||
|
List<ResolvedActivityInterval> activityIntervals = input.activityIntervals().stream()
|
||||||
|
.map(this::toResolvedActivityInterval)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = input.vehicleUsageIntervals().stream()
|
||||||
|
.map(this::toResolvedVehicleUsageInterval)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
List<ExtractedSupportEvent> supportEvents = input.supportEvidenceEvents().stream()
|
||||||
|
.map(this::toExtractedSupportEvent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
OffsetDateTime loadedFrom = input.loadedFrom() == null
|
||||||
|
? earliest(activityIntervals, vehicleUsageIntervals, supportEvents)
|
||||||
|
: utc(input.loadedFrom());
|
||||||
|
OffsetDateTime loadedTo = input.loadedTo() == null
|
||||||
|
? latest(activityIntervals, vehicleUsageIntervals, supportEvents)
|
||||||
|
: utc(input.loadedTo());
|
||||||
|
return new ResolvedDriverTimeline(
|
||||||
|
input.sourceKind(),
|
||||||
|
loadedFrom,
|
||||||
|
loadedTo,
|
||||||
|
vehicleUsageIntervals,
|
||||||
|
activityIntervals,
|
||||||
|
supportEvents,
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResolvedActivityInterval toResolvedActivityInterval(DriverWorkingTimeActivityInterval interval) {
|
||||||
|
if (interval == null || interval.startedAt() == null || interval.endedAt() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ResolvedActivityInterval(
|
||||||
|
interval.intervalId(),
|
||||||
|
utc(interval.startedAt()),
|
||||||
|
utc(interval.endedAt()),
|
||||||
|
interval.durationSeconds(),
|
||||||
|
interval.activityType(),
|
||||||
|
interval.cardSlot(),
|
||||||
|
interval.cardStatus(),
|
||||||
|
interval.drivingStatus(),
|
||||||
|
interval.registrationKey(),
|
||||||
|
interval.vehicleKey(),
|
||||||
|
interval.sourceKind(),
|
||||||
|
interval.sourceIntervalIds(),
|
||||||
|
interval.synthetic(),
|
||||||
|
interval.clippedToRequestedPeriod(),
|
||||||
|
interval.level()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResolvedVehicleUsageInterval toResolvedVehicleUsageInterval(DriverWorkingTimeVehicleUsageInterval interval) {
|
||||||
|
if (interval == null || interval.startedAt() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ResolvedVehicleUsageInterval(
|
||||||
|
safeSessionId(interval.sessionId()),
|
||||||
|
interval.driverKey(),
|
||||||
|
interval.intervalId(),
|
||||||
|
utc(interval.startedAt()),
|
||||||
|
utc(interval.endedAt()),
|
||||||
|
interval.durationSeconds(),
|
||||||
|
interval.odometerBeginKm(),
|
||||||
|
interval.odometerEndKm(),
|
||||||
|
interval.registrationKey(),
|
||||||
|
interval.vehicleKey(),
|
||||||
|
interval.sourceKind(),
|
||||||
|
interval.sourceIntervalIds()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtractedSupportEvent toExtractedSupportEvent(RuntimeSupportEvidenceEvent supportEvent) {
|
||||||
|
if (supportEvent == null || supportEvent.occurredAt() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object rawRecordPath = supportEvent.rawAttributes().get("rawRecordPath");
|
||||||
|
return new ExtractedSupportEvent(
|
||||||
|
supportEvent.eventId(),
|
||||||
|
supportEvent.driverKey(),
|
||||||
|
utc(supportEvent.occurredAt()),
|
||||||
|
supportEvent.eventDomain(),
|
||||||
|
supportEvent.eventType(),
|
||||||
|
supportEvent.lifecycle(),
|
||||||
|
null,
|
||||||
|
supportEvent.registrationKey(),
|
||||||
|
supportEvent.vehicleKey(),
|
||||||
|
supportEvent.countryCode(),
|
||||||
|
supportEvent.regionCode(),
|
||||||
|
supportEvent.countryFrom(),
|
||||||
|
supportEvent.countryTo(),
|
||||||
|
supportEvent.operation(),
|
||||||
|
supportEvent.latitude(),
|
||||||
|
supportEvent.longitude(),
|
||||||
|
null,
|
||||||
|
supportEvent.odometerKm(),
|
||||||
|
null,
|
||||||
|
supportEvent.speedKmh(),
|
||||||
|
supportEvent.maxSpeedKmh(),
|
||||||
|
rawRecordPath == null ? null : rawRecordPath.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimeDrivingInterruptionInterval mapDrivingInterruption(
|
||||||
|
TachographEsperDrivingInterruptionIntervalEvent value
|
||||||
|
) {
|
||||||
|
return new DriverWorkingTimeDrivingInterruptionInterval(
|
||||||
|
value.sessionId(),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimeVuCardAbsentInterval mapVuCardAbsent(TachographEsperVuCardAbsentIntervalEvent value) {
|
||||||
|
return new DriverWorkingTimeVuCardAbsentInterval(
|
||||||
|
value.sessionId(),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.previousUsageIntervalId(),
|
||||||
|
value.nextUsageIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimeRestCoverageInterval mapRestCoverage(
|
||||||
|
TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent value
|
||||||
|
) {
|
||||||
|
return new DriverWorkingTimeRestCoverageInterval(
|
||||||
|
value.sessionId(),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.cardAbsentDurationSeconds(),
|
||||||
|
value.cardAbsentCoveragePercent(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey(),
|
||||||
|
value.beginBoundaryOdometerKm(),
|
||||||
|
value.endBoundaryOdometerKm(),
|
||||||
|
value.beginGeoEventId(),
|
||||||
|
value.beginGeoEventDomain(),
|
||||||
|
value.beginGeoOccurredAt(),
|
||||||
|
value.beginLatitude(),
|
||||||
|
value.beginLongitude(),
|
||||||
|
value.beginGeoDistanceSeconds(),
|
||||||
|
value.beginGeoOdometerKm(),
|
||||||
|
value.endGeoEventId(),
|
||||||
|
value.endGeoEventDomain(),
|
||||||
|
value.endGeoOccurredAt(),
|
||||||
|
value.endLatitude(),
|
||||||
|
value.endLongitude(),
|
||||||
|
value.endGeoDistanceSeconds(),
|
||||||
|
value.endGeoOdometerKm(),
|
||||||
|
value.geoEvidenceMovementMeters(),
|
||||||
|
value.geoEvidenceMovementCategory(),
|
||||||
|
mapGeoEvidence(value.beginGeoEvent()),
|
||||||
|
mapGeoEvidence(value.endGeoEvent())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimeRestCoverageInterval mapPotentialHome(
|
||||||
|
TachographEsperPotentialHomeOvernightStayIntervalEvent value
|
||||||
|
) {
|
||||||
|
return new DriverWorkingTimeRestCoverageInterval(
|
||||||
|
value.sessionId(),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.cardAbsentDurationSeconds(),
|
||||||
|
value.cardAbsentCoveragePercent(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey(),
|
||||||
|
value.beginBoundaryOdometerKm(),
|
||||||
|
value.endBoundaryOdometerKm(),
|
||||||
|
value.beginGeoEventId(),
|
||||||
|
value.beginGeoEventDomain(),
|
||||||
|
value.beginGeoOccurredAt(),
|
||||||
|
value.beginLatitude(),
|
||||||
|
value.beginLongitude(),
|
||||||
|
value.beginGeoDistanceSeconds(),
|
||||||
|
value.beginGeoOdometerKm(),
|
||||||
|
value.endGeoEventId(),
|
||||||
|
value.endGeoEventDomain(),
|
||||||
|
value.endGeoOccurredAt(),
|
||||||
|
value.endLatitude(),
|
||||||
|
value.endLongitude(),
|
||||||
|
value.endGeoDistanceSeconds(),
|
||||||
|
value.endGeoOdometerKm(),
|
||||||
|
value.geoEvidenceMovementMeters(),
|
||||||
|
value.geoEvidenceMovementCategory(),
|
||||||
|
mapGeoEvidence(value.beginGeoEvent()),
|
||||||
|
mapGeoEvidence(value.endGeoEvent())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimeRestCoverageInterval mapPotentialInVehicle(
|
||||||
|
TachographEsperPotentialInVehicleOvernightStayIntervalEvent value
|
||||||
|
) {
|
||||||
|
return new DriverWorkingTimeRestCoverageInterval(
|
||||||
|
value.sessionId(),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.cardAbsentDurationSeconds(),
|
||||||
|
value.cardAbsentCoveragePercent(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey(),
|
||||||
|
value.beginBoundaryOdometerKm(),
|
||||||
|
value.endBoundaryOdometerKm(),
|
||||||
|
value.beginGeoEventId(),
|
||||||
|
value.beginGeoEventDomain(),
|
||||||
|
value.beginGeoOccurredAt(),
|
||||||
|
value.beginLatitude(),
|
||||||
|
value.beginLongitude(),
|
||||||
|
value.beginGeoDistanceSeconds(),
|
||||||
|
value.beginGeoOdometerKm(),
|
||||||
|
value.endGeoEventId(),
|
||||||
|
value.endGeoEventDomain(),
|
||||||
|
value.endGeoOccurredAt(),
|
||||||
|
value.endLatitude(),
|
||||||
|
value.endLongitude(),
|
||||||
|
value.endGeoDistanceSeconds(),
|
||||||
|
value.endGeoOdometerKm(),
|
||||||
|
value.geoEvidenceMovementMeters(),
|
||||||
|
value.geoEvidenceMovementCategory(),
|
||||||
|
mapGeoEvidence(value.beginGeoEvent()),
|
||||||
|
mapGeoEvidence(value.endGeoEvent())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimePotentialInVehicleTripInterval mapPotentialTrip(
|
||||||
|
TachographEsperPotentialInVehicleTripIntervalEvent value
|
||||||
|
) {
|
||||||
|
return new DriverWorkingTimePotentialInVehicleTripInterval(
|
||||||
|
value.sessionId(),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.registrationKey(),
|
||||||
|
value.vehicleKey(),
|
||||||
|
value.containedPotentialInVehicleOvernightStayIntervalCount(),
|
||||||
|
value.containedPotentialInVehicleOvernightStayDurationSeconds(),
|
||||||
|
value.containedCardAbsentDurationSeconds(),
|
||||||
|
value.firstPotentialInVehicleOvernightStayStartedAt(),
|
||||||
|
value.lastPotentialInVehicleOvernightStayEndedAt(),
|
||||||
|
value.firstPreviousDrivingSourceIntervalId(),
|
||||||
|
value.lastNextDrivingSourceIntervalId(),
|
||||||
|
value.potentialInVehicleOvernightStayIntervals().stream().map(this::mapPotentialInVehicle).toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DriverWorkingTimeGeoEvidenceEvent mapGeoEvidence(TachographEsperGeoEvidenceEvent value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new DriverWorkingTimeGeoEvidenceEvent(
|
||||||
|
value.eventId(),
|
||||||
|
value.eventDomain(),
|
||||||
|
value.occurredAt(),
|
||||||
|
value.latitude(),
|
||||||
|
value.longitude(),
|
||||||
|
value.distanceSeconds(),
|
||||||
|
value.odometerKm()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime earliest(
|
||||||
|
List<ResolvedActivityInterval> activityIntervals,
|
||||||
|
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
|
||||||
|
List<ExtractedSupportEvent> supportEvents
|
||||||
|
) {
|
||||||
|
OffsetDateTime earliest = null;
|
||||||
|
for (ResolvedActivityInterval interval : activityIntervals) {
|
||||||
|
earliest = min(earliest, interval.from());
|
||||||
|
}
|
||||||
|
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
||||||
|
earliest = min(earliest, interval.from());
|
||||||
|
}
|
||||||
|
for (ExtractedSupportEvent event : supportEvents) {
|
||||||
|
earliest = min(earliest, event.occurredAt());
|
||||||
|
}
|
||||||
|
return earliest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime latest(
|
||||||
|
List<ResolvedActivityInterval> activityIntervals,
|
||||||
|
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
|
||||||
|
List<ExtractedSupportEvent> supportEvents
|
||||||
|
) {
|
||||||
|
OffsetDateTime latest = null;
|
||||||
|
for (ResolvedActivityInterval interval : activityIntervals) {
|
||||||
|
latest = max(latest, interval.to());
|
||||||
|
}
|
||||||
|
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
||||||
|
latest = max(latest, interval.to());
|
||||||
|
}
|
||||||
|
for (ExtractedSupportEvent event : supportEvents) {
|
||||||
|
latest = max(latest, event.occurredAt());
|
||||||
|
}
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID safeSessionId(UUID sessionId) {
|
||||||
|
return sessionId == null ? new UUID(0L, 0L) : sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime utc(OffsetDateTime value) {
|
||||||
|
return value == null ? null : value.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime max(OffsetDateTime left, OffsetDateTime right) {
|
||||||
|
if (left == null) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right == null) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left.isAfter(right) ? left : right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OffsetDateTime min(OffsetDateTime left, OffsetDateTime right) {
|
||||||
|
if (left == null) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right == null) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left.isBefore(right) ? left : right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
package at.procon.eventhub.processing.driverworkingtime.service;
|
package at.procon.eventhub.processing.driverworkingtime.service;
|
||||||
|
|
||||||
import at.procon.eventhub.config.EventHubProperties;
|
|
||||||
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
|
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeDerivedProjectionBundle;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeDrivingInterruptionInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeGeoEvidenceEvent;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimePotentialInVehicleTripInterval;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeRestCoverageInterval;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVuCardAbsentInterval;
|
||||||
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceEvent;
|
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.*;
|
import at.procon.eventhub.tachographfilesession.model.*;
|
||||||
import at.procon.eventhub.tachographfilesession.service.DriverTimelineBuilder;
|
|
||||||
import at.procon.eventhub.tachographfilesession.service.DriverTimelineReusableProjectionBuilder;
|
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographEsperProcessingInput;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
@ -31,76 +32,59 @@ import org.springframework.stereotype.Service;
|
||||||
@Service
|
@Service
|
||||||
public class DriverWorkingTimeProcessingCore {
|
public class DriverWorkingTimeProcessingCore {
|
||||||
|
|
||||||
private final DriverTimelineBuilder driverTimelineBuilder;
|
private final DriverWorkingTimeDerivedProjectionEngine derivedProjectionEngine;
|
||||||
private final DriverTimelineReusableProjectionBuilder reusableProjectionBuilder;
|
|
||||||
private final EventHubProperties properties;
|
|
||||||
|
|
||||||
public DriverWorkingTimeProcessingCore(
|
public DriverWorkingTimeProcessingCore(
|
||||||
DriverTimelineBuilder driverTimelineBuilder,
|
DriverWorkingTimeDerivedProjectionEngine derivedProjectionEngine
|
||||||
DriverTimelineReusableProjectionBuilder reusableProjectionBuilder,
|
|
||||||
EventHubProperties properties
|
|
||||||
) {
|
) {
|
||||||
this.driverTimelineBuilder = driverTimelineBuilder;
|
this.derivedProjectionEngine = derivedProjectionEngine;
|
||||||
this.reusableProjectionBuilder = reusableProjectionBuilder;
|
|
||||||
this.properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DriverWorkingTimeProcessingResultDto process(TachographEsperProcessingInput input) {
|
|
||||||
Objects.requireNonNull(input, "input must not be null");
|
|
||||||
return process(toSourceNeutralInput(input));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DriverWorkingTimeProcessingResultDto process(DriverWorkingTimeProcessingInput input) {
|
public DriverWorkingTimeProcessingResultDto process(DriverWorkingTimeProcessingInput input) {
|
||||||
Objects.requireNonNull(input, "input must not be null");
|
Objects.requireNonNull(input, "input must not be null");
|
||||||
ResolvedDriverTimeline timeline = toResolvedTimeline(input);
|
OffsetDateTime loadedFrom = input.loadedFrom();
|
||||||
|
OffsetDateTime loadedTo = input.loadedTo();
|
||||||
String driverKey = input.driverKey();
|
String driverKey = input.driverKey();
|
||||||
OffsetDateTime requestedFrom = input.requestedFrom() == null ? timeline.loadedFrom() : utc(input.requestedFrom());
|
OffsetDateTime requestedFrom = input.requestedFrom() == null ? loadedFrom : utc(input.requestedFrom());
|
||||||
OffsetDateTime requestedTo = input.requestedTo() == null ? timeline.loadedTo() : utc(input.requestedTo());
|
OffsetDateTime requestedTo = input.requestedTo() == null ? loadedTo : utc(input.requestedTo());
|
||||||
if (requestedFrom != null && requestedTo != null && requestedTo.isBefore(requestedFrom)) {
|
if (requestedFrom != null && requestedTo != null && requestedTo.isBefore(requestedFrom)) {
|
||||||
throw new IllegalArgumentException("occurredTo must not be before occurredFrom.");
|
throw new IllegalArgumentException("occurredTo must not be before occurredFrom.");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TachographEsperActivityIntervalEvent> activityIntervals = clipEsperActivityIntervalEvents(
|
List<TachographEsperActivityIntervalEvent> activityIntervals = clipEsperActivityIntervalEvents(
|
||||||
driverTimelineBuilder.buildEsperActivityIntervalEvents(input.sessionId(), driverKey, timeline),
|
buildEsperActivityIntervalEvents(input.activityIntervals()),
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo
|
requestedTo
|
||||||
);
|
);
|
||||||
List<TachographEsperActivityIntervalEvent> drivingIntervals = clipEsperActivityIntervalEvents(
|
List<TachographEsperActivityIntervalEvent> drivingIntervals = clipEsperActivityIntervalEvents(
|
||||||
driverTimelineBuilder.buildEsperDrivingIntervalEvents(input.sessionId(), driverKey, timeline),
|
buildEsperDrivingIntervalEvents(input.activityIntervals()),
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo
|
requestedTo
|
||||||
);
|
);
|
||||||
|
|
||||||
TachographEsperDrivingDerivedProjectionBundle derivedProjectionBundle =
|
DriverWorkingTimeDerivedProjectionBundle derivedProjectionBundle = derivedProjectionEngine.build(input);
|
||||||
reusableProjectionBuilder.buildEsperDrivingDerivedProjectionBundle(
|
|
||||||
safeSessionId(input.sessionId()),
|
|
||||||
driverKey,
|
|
||||||
timeline,
|
|
||||||
input.significantDrivingMinutes(),
|
|
||||||
input.minimumRestPeriodMinutes()
|
|
||||||
);
|
|
||||||
|
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionIntervals =
|
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionIntervals =
|
||||||
derivedProjectionBundle.drivingInterruptionIntervals();
|
derivedProjectionBundle.drivingInterruptionIntervals().stream().map(this::toLegacy).toList();
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals =
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals =
|
||||||
clipEsperDrivingInterruptionIntervalEvents(rawDrivingInterruptionIntervals, requestedFrom, requestedTo);
|
clipEsperDrivingInterruptionIntervalEvents(rawDrivingInterruptionIntervals, requestedFrom, requestedTo);
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDailyWeeklyRestCandidateIntervals =
|
List<TachographEsperDrivingInterruptionIntervalEvent> rawDailyWeeklyRestCandidateIntervals =
|
||||||
derivedProjectionBundle.dailyWeeklyRestCandidateIntervals();
|
derivedProjectionBundle.dailyWeeklyRestCandidateIntervals().stream().map(this::toLegacy).toList();
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals =
|
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals =
|
||||||
clipEsperDrivingInterruptionIntervalEvents(rawDailyWeeklyRestCandidateIntervals, requestedFrom, requestedTo);
|
clipEsperDrivingInterruptionIntervalEvents(rawDailyWeeklyRestCandidateIntervals, requestedFrom, requestedTo);
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionVehicleChangeIntervals =
|
List<TachographEsperDrivingInterruptionIntervalEvent> rawDrivingInterruptionVehicleChangeIntervals =
|
||||||
derivedProjectionBundle.drivingInterruptionVehicleChangeIntervals();
|
derivedProjectionBundle.drivingInterruptionVehicleChangeIntervals().stream().map(this::toLegacy).toList();
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals =
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals =
|
||||||
clipEsperDrivingInterruptionIntervalEvents(rawDrivingInterruptionVehicleChangeIntervals, requestedFrom, requestedTo);
|
clipEsperDrivingInterruptionIntervalEvents(rawDrivingInterruptionVehicleChangeIntervals, requestedFrom, requestedTo);
|
||||||
|
|
||||||
List<TachographEsperVehicleUsageIntervalEvent> rawVehicleUsageIntervals =
|
List<TachographEsperVehicleUsageIntervalEvent> rawVehicleUsageIntervals =
|
||||||
driverTimelineBuilder.buildEsperVehicleUsageIntervalEvents(timeline);
|
buildEsperVehicleUsageIntervalEvents(input.vehicleUsageIntervals());
|
||||||
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals =
|
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals =
|
||||||
derivedProjectionBundle.vuCardAbsentIntervals();
|
derivedProjectionBundle.vuCardAbsentIntervals().stream().map(this::toLegacy).toList();
|
||||||
|
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals =
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals =
|
||||||
clipEsperPotentialHomeOvernightStayIntervalEvents(
|
clipEsperPotentialHomeOvernightStayIntervalEvents(
|
||||||
derivedProjectionBundle.potentialHomeOvernightStayIntervals(),
|
derivedProjectionBundle.potentialHomeOvernightStayIntervals().stream().map(this::toLegacyPotentialHome).toList(),
|
||||||
rawVuCardAbsentIntervals,
|
rawVuCardAbsentIntervals,
|
||||||
rawVehicleUsageIntervals,
|
rawVehicleUsageIntervals,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
|
|
@ -108,7 +92,7 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
);
|
);
|
||||||
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> dailyWeeklyRestCandidateCoverageIntervals =
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> dailyWeeklyRestCandidateCoverageIntervals =
|
||||||
clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
||||||
derivedProjectionBundle.dailyWeeklyRestCandidateCoverageIntervals(),
|
derivedProjectionBundle.dailyWeeklyRestCandidateCoverageIntervals().stream().map(this::toLegacyCoverage).toList(),
|
||||||
rawVuCardAbsentIntervals,
|
rawVuCardAbsentIntervals,
|
||||||
rawVehicleUsageIntervals,
|
rawVehicleUsageIntervals,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
|
|
@ -116,7 +100,7 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
);
|
);
|
||||||
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> unclassifiedDailyWeeklyRestCandidateCoverageIntervals =
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> unclassifiedDailyWeeklyRestCandidateCoverageIntervals =
|
||||||
clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
||||||
derivedProjectionBundle.unclassifiedDailyWeeklyRestCandidateCoverageIntervals(),
|
derivedProjectionBundle.unclassifiedDailyWeeklyRestCandidateCoverageIntervals().stream().map(this::toLegacyCoverage).toList(),
|
||||||
rawVuCardAbsentIntervals,
|
rawVuCardAbsentIntervals,
|
||||||
rawVehicleUsageIntervals,
|
rawVehicleUsageIntervals,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
|
|
@ -124,7 +108,7 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
);
|
);
|
||||||
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> potentialInVehicleOvernightStayIntervals =
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> potentialInVehicleOvernightStayIntervals =
|
||||||
clipEsperPotentialInVehicleOvernightStayIntervalEvents(
|
clipEsperPotentialInVehicleOvernightStayIntervalEvents(
|
||||||
derivedProjectionBundle.potentialInVehicleOvernightStayIntervals(),
|
derivedProjectionBundle.potentialInVehicleOvernightStayIntervals().stream().map(this::toLegacyPotentialInVehicle).toList(),
|
||||||
rawVuCardAbsentIntervals,
|
rawVuCardAbsentIntervals,
|
||||||
rawVehicleUsageIntervals,
|
rawVehicleUsageIntervals,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
|
|
@ -132,7 +116,7 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
);
|
);
|
||||||
List<TachographEsperPotentialInVehicleTripIntervalEvent> potentialInVehicleTripIntervals =
|
List<TachographEsperPotentialInVehicleTripIntervalEvent> potentialInVehicleTripIntervals =
|
||||||
clipEsperPotentialInVehicleTripIntervalEvents(
|
clipEsperPotentialInVehicleTripIntervalEvents(
|
||||||
derivedProjectionBundle.potentialInVehicleTripIntervals(),
|
derivedProjectionBundle.potentialInVehicleTripIntervals().stream().map(this::toLegacy).toList(),
|
||||||
potentialInVehicleOvernightStayIntervals,
|
potentialInVehicleOvernightStayIntervals,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo
|
requestedTo
|
||||||
|
|
@ -148,7 +132,7 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
requestedTo
|
requestedTo
|
||||||
);
|
);
|
||||||
List<TachographEsperSupportGeoEvent> supportGeoEvents = clipEsperSupportGeoEvents(
|
List<TachographEsperSupportGeoEvent> supportGeoEvents = clipEsperSupportGeoEvents(
|
||||||
timeline.supportEvents(),
|
buildSupportGeoEvents(input.supportEvidenceEvents()),
|
||||||
driverKey,
|
driverKey,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo
|
requestedTo
|
||||||
|
|
@ -157,9 +141,9 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
return new DriverWorkingTimeProcessingResultDto(
|
return new DriverWorkingTimeProcessingResultDto(
|
||||||
input.sessionId(),
|
input.sessionId(),
|
||||||
driverKey,
|
driverKey,
|
||||||
timeline.sourceKind(),
|
input.sourceKind(),
|
||||||
timeline.loadedFrom(),
|
loadedFrom,
|
||||||
timeline.loadedTo(),
|
loadedTo,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo,
|
requestedTo,
|
||||||
activityIntervals.size(),
|
activityIntervals.size(),
|
||||||
|
|
@ -192,207 +176,297 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DriverWorkingTimeProcessingResultDto processDriverWorkingTime(TachographEsperProcessingInput input) {
|
public DriverWorkingTimeProcessingResultDto processDriverWorkingTime(DriverWorkingTimeProcessingInput input) {
|
||||||
return process(input);
|
return process(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DriverWorkingTimeProcessingInput toSourceNeutralInput(TachographEsperProcessingInput input) {
|
private List<TachographEsperActivityIntervalEvent> buildEsperActivityIntervalEvents(
|
||||||
ResolvedDriverTimeline timeline = Objects.requireNonNull(input.timeline(), "timeline must not be null");
|
List<DriverWorkingTimeActivityInterval> intervals
|
||||||
String driverKey = input.driverKey();
|
|
||||||
return new DriverWorkingTimeProcessingInput(
|
|
||||||
input.sessionId(),
|
|
||||||
driverKey,
|
|
||||||
timeline.sourceKind(),
|
|
||||||
timeline.loadedFrom(),
|
|
||||||
timeline.loadedTo(),
|
|
||||||
input.requestedFrom(),
|
|
||||||
input.requestedTo(),
|
|
||||||
input.significantDrivingMinutes(),
|
|
||||||
input.minimumRestPeriodMinutes(),
|
|
||||||
safeList(timeline.activityIntervals()).stream()
|
|
||||||
.map(interval -> DriverWorkingTimeActivityInterval.fromResolved(input.sessionId(), driverKey, interval))
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList(),
|
|
||||||
safeList(timeline.vehicleUsageIntervals()).stream()
|
|
||||||
.map(DriverWorkingTimeVehicleUsageInterval::fromResolved)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList(),
|
|
||||||
safeList(timeline.supportEvents()).stream()
|
|
||||||
.map(this::toSupportEvidenceEvent)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList(),
|
|
||||||
input.notes()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolvedDriverTimeline toResolvedTimeline(DriverWorkingTimeProcessingInput input) {
|
|
||||||
List<ResolvedActivityInterval> activityIntervals = input.activityIntervals().stream()
|
|
||||||
.map(this::toResolvedActivityInterval)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList();
|
|
||||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals = input.vehicleUsageIntervals().stream()
|
|
||||||
.map(this::toResolvedVehicleUsageInterval)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList();
|
|
||||||
List<ExtractedSupportEvent> supportEvents = input.supportEvidenceEvents().stream()
|
|
||||||
.map(this::toExtractedSupportEvent)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList();
|
|
||||||
OffsetDateTime loadedFrom = input.loadedFrom() == null
|
|
||||||
? earliest(activityIntervals, vehicleUsageIntervals, supportEvents)
|
|
||||||
: utc(input.loadedFrom());
|
|
||||||
OffsetDateTime loadedTo = input.loadedTo() == null
|
|
||||||
? latest(activityIntervals, vehicleUsageIntervals, supportEvents)
|
|
||||||
: utc(input.loadedTo());
|
|
||||||
return new ResolvedDriverTimeline(
|
|
||||||
input.sourceKind(),
|
|
||||||
loadedFrom,
|
|
||||||
loadedTo,
|
|
||||||
vehicleUsageIntervals,
|
|
||||||
activityIntervals,
|
|
||||||
supportEvents,
|
|
||||||
List.of()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolvedActivityInterval toResolvedActivityInterval(DriverWorkingTimeActivityInterval interval) {
|
|
||||||
if (interval == null || interval.startedAt() == null || interval.endedAt() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new ResolvedActivityInterval(
|
|
||||||
interval.intervalId(),
|
|
||||||
utc(interval.startedAt()),
|
|
||||||
utc(interval.endedAt()),
|
|
||||||
interval.durationSeconds(),
|
|
||||||
interval.activityType(),
|
|
||||||
interval.cardSlot(),
|
|
||||||
interval.cardStatus(),
|
|
||||||
interval.drivingStatus(),
|
|
||||||
interval.registrationKey(),
|
|
||||||
interval.vehicleKey(),
|
|
||||||
interval.sourceKind(),
|
|
||||||
interval.sourceIntervalIds(),
|
|
||||||
interval.synthetic(),
|
|
||||||
interval.clippedToRequestedPeriod(),
|
|
||||||
interval.level()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolvedVehicleUsageInterval toResolvedVehicleUsageInterval(DriverWorkingTimeVehicleUsageInterval interval) {
|
|
||||||
if (interval == null || interval.startedAt() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new ResolvedVehicleUsageInterval(
|
|
||||||
safeSessionId(interval.sessionId()),
|
|
||||||
interval.driverKey(),
|
|
||||||
interval.intervalId(),
|
|
||||||
utc(interval.startedAt()),
|
|
||||||
utc(interval.endedAt()),
|
|
||||||
interval.durationSeconds(),
|
|
||||||
interval.odometerBeginKm(),
|
|
||||||
interval.odometerEndKm(),
|
|
||||||
interval.registrationKey(),
|
|
||||||
interval.vehicleKey(),
|
|
||||||
interval.sourceKind(),
|
|
||||||
interval.sourceIntervalIds()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
utc(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(),
|
|
||||||
Map.of("rawRecordPath", supportEvent.rawRecordPath())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExtractedSupportEvent toExtractedSupportEvent(RuntimeSupportEvidenceEvent supportEvent) {
|
|
||||||
if (supportEvent == null || supportEvent.occurredAt() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Object rawRecordPath = supportEvent.rawAttributes().get("rawRecordPath");
|
|
||||||
return new ExtractedSupportEvent(
|
|
||||||
supportEvent.eventId(),
|
|
||||||
supportEvent.driverKey(),
|
|
||||||
utc(supportEvent.occurredAt()),
|
|
||||||
supportEvent.eventDomain(),
|
|
||||||
supportEvent.eventType(),
|
|
||||||
supportEvent.lifecycle(),
|
|
||||||
null,
|
|
||||||
supportEvent.registrationKey(),
|
|
||||||
supportEvent.vehicleKey(),
|
|
||||||
supportEvent.countryCode(),
|
|
||||||
supportEvent.regionCode(),
|
|
||||||
supportEvent.countryFrom(),
|
|
||||||
supportEvent.countryTo(),
|
|
||||||
supportEvent.operation(),
|
|
||||||
supportEvent.latitude(),
|
|
||||||
supportEvent.longitude(),
|
|
||||||
null,
|
|
||||||
supportEvent.odometerKm(),
|
|
||||||
null,
|
|
||||||
supportEvent.speedKmh(),
|
|
||||||
supportEvent.maxSpeedKmh(),
|
|
||||||
rawRecordPath == null ? null : rawRecordPath.toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OffsetDateTime earliest(
|
|
||||||
List<ResolvedActivityInterval> activityIntervals,
|
|
||||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
|
|
||||||
List<ExtractedSupportEvent> supportEvents
|
|
||||||
) {
|
) {
|
||||||
OffsetDateTime earliest = null;
|
return safeList(intervals).stream()
|
||||||
for (ResolvedActivityInterval interval : activityIntervals) {
|
.filter(interval -> interval.startedAt() != null && interval.endedAt() != null)
|
||||||
earliest = min(earliest, interval.from());
|
.map(interval -> new TachographEsperActivityIntervalEvent(
|
||||||
}
|
safeSessionId(interval.sessionId()),
|
||||||
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
interval.driverKey(),
|
||||||
earliest = min(earliest, interval.from());
|
interval.intervalId(),
|
||||||
}
|
interval.activityType(),
|
||||||
for (ExtractedSupportEvent event : supportEvents) {
|
interval.cardSlot(),
|
||||||
earliest = min(earliest, event.occurredAt());
|
interval.cardStatus(),
|
||||||
}
|
interval.drivingStatus(),
|
||||||
return earliest;
|
interval.registrationKey(),
|
||||||
|
interval.vehicleKey(),
|
||||||
|
interval.sourceKind(),
|
||||||
|
utc(interval.startedAt()),
|
||||||
|
utc(interval.endedAt()),
|
||||||
|
interval.durationSeconds(),
|
||||||
|
interval.sourceIntervalIds(),
|
||||||
|
interval.synthetic(),
|
||||||
|
interval.clippedToRequestedPeriod(),
|
||||||
|
interval.level()
|
||||||
|
))
|
||||||
|
.sorted(Comparator.comparing(TachographEsperActivityIntervalEvent::startedAt)
|
||||||
|
.thenComparing(TachographEsperActivityIntervalEvent::endedAt))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OffsetDateTime latest(
|
private List<TachographEsperActivityIntervalEvent> buildEsperDrivingIntervalEvents(
|
||||||
List<ResolvedActivityInterval> activityIntervals,
|
List<DriverWorkingTimeActivityInterval> intervals
|
||||||
List<ResolvedVehicleUsageInterval> vehicleUsageIntervals,
|
|
||||||
List<ExtractedSupportEvent> supportEvents
|
|
||||||
) {
|
) {
|
||||||
OffsetDateTime latest = null;
|
return buildEsperActivityIntervalEvents(intervals).stream()
|
||||||
for (ResolvedActivityInterval interval : activityIntervals) {
|
.filter(interval -> Objects.equals("DRIVE", interval.activityType()))
|
||||||
latest = max(latest, interval.to());
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TachographEsperVehicleUsageIntervalEvent> buildEsperVehicleUsageIntervalEvents(
|
||||||
|
List<DriverWorkingTimeVehicleUsageInterval> intervals
|
||||||
|
) {
|
||||||
|
return safeList(intervals).stream()
|
||||||
|
.filter(interval -> interval.startedAt() != null)
|
||||||
|
.map(interval -> new TachographEsperVehicleUsageIntervalEvent(
|
||||||
|
safeSessionId(interval.sessionId()),
|
||||||
|
interval.driverKey(),
|
||||||
|
interval.intervalId(),
|
||||||
|
utc(interval.startedAt()),
|
||||||
|
utc(interval.endedAt()),
|
||||||
|
interval.durationSeconds(),
|
||||||
|
interval.odometerBeginKm(),
|
||||||
|
interval.odometerEndKm(),
|
||||||
|
interval.registrationKey(),
|
||||||
|
interval.vehicleKey(),
|
||||||
|
interval.sourceKind(),
|
||||||
|
interval.sourceIntervalIds()
|
||||||
|
))
|
||||||
|
.sorted(Comparator.comparing(TachographEsperVehicleUsageIntervalEvent::startedAt)
|
||||||
|
.thenComparing(TachographEsperVehicleUsageIntervalEvent::endedAt, Comparator.nullsLast(Comparator.naturalOrder())))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TachographEsperSupportGeoEvent> buildSupportGeoEvents(
|
||||||
|
List<RuntimeSupportEvidenceEvent> supportEvents
|
||||||
|
) {
|
||||||
|
return safeList(supportEvents).stream()
|
||||||
|
.filter(event -> event.occurredAt() != null)
|
||||||
|
.map(event -> new TachographEsperSupportGeoEvent(
|
||||||
|
event.eventId(),
|
||||||
|
event.driverKey(),
|
||||||
|
utc(event.occurredAt()),
|
||||||
|
event.eventDomain(),
|
||||||
|
event.eventType(),
|
||||||
|
event.lifecycle(),
|
||||||
|
event.registrationKey(),
|
||||||
|
event.vehicleKey(),
|
||||||
|
event.countryCode(),
|
||||||
|
event.regionCode(),
|
||||||
|
event.countryFrom(),
|
||||||
|
event.countryTo(),
|
||||||
|
event.operation(),
|
||||||
|
event.latitude(),
|
||||||
|
event.longitude(),
|
||||||
|
event.odometerKm(),
|
||||||
|
rawRecordPath(event)
|
||||||
|
))
|
||||||
|
.sorted(Comparator.comparing(TachographEsperSupportGeoEvent::occurredAt)
|
||||||
|
.thenComparing(TachographEsperSupportGeoEvent::eventId, Comparator.nullsLast(String::compareTo)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String rawRecordPath(RuntimeSupportEvidenceEvent event) {
|
||||||
|
if (event == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
for (ResolvedVehicleUsageInterval interval : vehicleUsageIntervals) {
|
Object value = event.rawAttributes().get("rawRecordPath");
|
||||||
latest = max(latest, interval.to());
|
return value == null ? null : value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperDrivingInterruptionIntervalEvent toLegacy(DriverWorkingTimeDrivingInterruptionInterval value) {
|
||||||
|
return new TachographEsperDrivingInterruptionIntervalEvent(
|
||||||
|
safeSessionId(value.sessionId()),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperVuCardAbsentIntervalEvent toLegacy(DriverWorkingTimeVuCardAbsentInterval value) {
|
||||||
|
return new TachographEsperVuCardAbsentIntervalEvent(
|
||||||
|
safeSessionId(value.sessionId()),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.previousUsageIntervalId(),
|
||||||
|
value.nextUsageIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent toLegacyCoverage(
|
||||||
|
DriverWorkingTimeRestCoverageInterval value
|
||||||
|
) {
|
||||||
|
return new TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||||
|
safeSessionId(value.sessionId()),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.cardAbsentDurationSeconds(),
|
||||||
|
value.cardAbsentCoveragePercent(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey(),
|
||||||
|
value.beginBoundaryOdometerKm(),
|
||||||
|
value.endBoundaryOdometerKm(),
|
||||||
|
value.beginGeoEventId(),
|
||||||
|
value.beginGeoEventDomain(),
|
||||||
|
value.beginGeoOccurredAt(),
|
||||||
|
value.beginLatitude(),
|
||||||
|
value.beginLongitude(),
|
||||||
|
value.beginGeoDistanceSeconds(),
|
||||||
|
value.beginGeoOdometerKm(),
|
||||||
|
value.endGeoEventId(),
|
||||||
|
value.endGeoEventDomain(),
|
||||||
|
value.endGeoOccurredAt(),
|
||||||
|
value.endLatitude(),
|
||||||
|
value.endLongitude(),
|
||||||
|
value.endGeoDistanceSeconds(),
|
||||||
|
value.endGeoOdometerKm(),
|
||||||
|
value.geoEvidenceMovementMeters(),
|
||||||
|
value.geoEvidenceMovementCategory(),
|
||||||
|
toLegacy(value.beginGeoEvent()),
|
||||||
|
toLegacy(value.endGeoEvent())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperPotentialHomeOvernightStayIntervalEvent toLegacyPotentialHome(
|
||||||
|
DriverWorkingTimeRestCoverageInterval value
|
||||||
|
) {
|
||||||
|
return new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||||
|
safeSessionId(value.sessionId()),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.cardAbsentDurationSeconds(),
|
||||||
|
value.cardAbsentCoveragePercent(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey(),
|
||||||
|
value.beginBoundaryOdometerKm(),
|
||||||
|
value.endBoundaryOdometerKm(),
|
||||||
|
value.beginGeoEventId(),
|
||||||
|
value.beginGeoEventDomain(),
|
||||||
|
value.beginGeoOccurredAt(),
|
||||||
|
value.beginLatitude(),
|
||||||
|
value.beginLongitude(),
|
||||||
|
value.beginGeoDistanceSeconds(),
|
||||||
|
value.beginGeoOdometerKm(),
|
||||||
|
value.endGeoEventId(),
|
||||||
|
value.endGeoEventDomain(),
|
||||||
|
value.endGeoOccurredAt(),
|
||||||
|
value.endLatitude(),
|
||||||
|
value.endLongitude(),
|
||||||
|
value.endGeoDistanceSeconds(),
|
||||||
|
value.endGeoOdometerKm(),
|
||||||
|
value.geoEvidenceMovementMeters(),
|
||||||
|
value.geoEvidenceMovementCategory(),
|
||||||
|
toLegacy(value.beginGeoEvent()),
|
||||||
|
toLegacy(value.endGeoEvent())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperPotentialInVehicleOvernightStayIntervalEvent toLegacyPotentialInVehicle(
|
||||||
|
DriverWorkingTimeRestCoverageInterval value
|
||||||
|
) {
|
||||||
|
return new TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
||||||
|
safeSessionId(value.sessionId()),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.cardAbsentDurationSeconds(),
|
||||||
|
value.cardAbsentCoveragePercent(),
|
||||||
|
value.previousDrivingSourceIntervalId(),
|
||||||
|
value.nextDrivingSourceIntervalId(),
|
||||||
|
value.previousRegistrationKey(),
|
||||||
|
value.nextRegistrationKey(),
|
||||||
|
value.previousVehicleKey(),
|
||||||
|
value.nextVehicleKey(),
|
||||||
|
value.beginBoundaryOdometerKm(),
|
||||||
|
value.endBoundaryOdometerKm(),
|
||||||
|
value.beginGeoEventId(),
|
||||||
|
value.beginGeoEventDomain(),
|
||||||
|
value.beginGeoOccurredAt(),
|
||||||
|
value.beginLatitude(),
|
||||||
|
value.beginLongitude(),
|
||||||
|
value.beginGeoDistanceSeconds(),
|
||||||
|
value.beginGeoOdometerKm(),
|
||||||
|
value.endGeoEventId(),
|
||||||
|
value.endGeoEventDomain(),
|
||||||
|
value.endGeoOccurredAt(),
|
||||||
|
value.endLatitude(),
|
||||||
|
value.endLongitude(),
|
||||||
|
value.endGeoDistanceSeconds(),
|
||||||
|
value.endGeoOdometerKm(),
|
||||||
|
value.geoEvidenceMovementMeters(),
|
||||||
|
value.geoEvidenceMovementCategory(),
|
||||||
|
toLegacy(value.beginGeoEvent()),
|
||||||
|
toLegacy(value.endGeoEvent())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperPotentialInVehicleTripIntervalEvent toLegacy(
|
||||||
|
DriverWorkingTimePotentialInVehicleTripInterval value
|
||||||
|
) {
|
||||||
|
return new TachographEsperPotentialInVehicleTripIntervalEvent(
|
||||||
|
safeSessionId(value.sessionId()),
|
||||||
|
value.driverKey(),
|
||||||
|
value.startedAt(),
|
||||||
|
value.endedAt(),
|
||||||
|
value.durationSeconds(),
|
||||||
|
value.registrationKey(),
|
||||||
|
value.vehicleKey(),
|
||||||
|
value.containedPotentialInVehicleOvernightStayIntervalCount(),
|
||||||
|
value.containedPotentialInVehicleOvernightStayDurationSeconds(),
|
||||||
|
value.containedCardAbsentDurationSeconds(),
|
||||||
|
value.firstPotentialInVehicleOvernightStayStartedAt(),
|
||||||
|
value.lastPotentialInVehicleOvernightStayEndedAt(),
|
||||||
|
value.firstPreviousDrivingSourceIntervalId(),
|
||||||
|
value.lastNextDrivingSourceIntervalId(),
|
||||||
|
value.potentialInVehicleOvernightStayIntervals().stream()
|
||||||
|
.map(this::toLegacyPotentialInVehicle)
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographEsperGeoEvidenceEvent toLegacy(DriverWorkingTimeGeoEvidenceEvent value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
for (ExtractedSupportEvent event : supportEvents) {
|
return new TachographEsperGeoEvidenceEvent(
|
||||||
latest = max(latest, event.occurredAt());
|
value.eventId(),
|
||||||
}
|
value.eventDomain(),
|
||||||
return latest;
|
value.occurredAt(),
|
||||||
|
value.latitude(),
|
||||||
|
value.longitude(),
|
||||||
|
value.distanceSeconds(),
|
||||||
|
value.odometerKm()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID safeSessionId(UUID sessionId) {
|
private UUID safeSessionId(UUID sessionId) {
|
||||||
|
|
@ -498,7 +572,7 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TachographEsperSupportGeoEvent> clipEsperSupportGeoEvents(
|
private List<TachographEsperSupportGeoEvent> clipEsperSupportGeoEvents(
|
||||||
List<ExtractedSupportEvent> supportEvents,
|
List<TachographEsperSupportGeoEvent> supportEvents,
|
||||||
String driverKey,
|
String driverKey,
|
||||||
OffsetDateTime requestedFrom,
|
OffsetDateTime requestedFrom,
|
||||||
OffsetDateTime requestedTo
|
OffsetDateTime requestedTo
|
||||||
|
|
@ -511,25 +585,6 @@ public class DriverWorkingTimeProcessingCore {
|
||||||
.filter(event -> event.occurredAt() != null)
|
.filter(event -> event.occurredAt() != null)
|
||||||
.filter(event -> event.latitude() != null && event.longitude() != null)
|
.filter(event -> event.latitude() != null && event.longitude() != null)
|
||||||
.filter(event -> !event.occurredAt().isBefore(requestedFrom) && !event.occurredAt().isAfter(requestedTo))
|
.filter(event -> !event.occurredAt().isBefore(requestedFrom) && !event.occurredAt().isAfter(requestedTo))
|
||||||
.map(event -> new TachographEsperSupportGeoEvent(
|
|
||||||
event.eventId(),
|
|
||||||
event.driverKey(),
|
|
||||||
event.occurredAt(),
|
|
||||||
event.eventDomain(),
|
|
||||||
event.eventType(),
|
|
||||||
event.eventLifecycle(),
|
|
||||||
event.registrationKey(),
|
|
||||||
event.vehicleKey(),
|
|
||||||
event.country(),
|
|
||||||
event.region(),
|
|
||||||
event.countryFrom(),
|
|
||||||
event.countryTo(),
|
|
||||||
event.operation(),
|
|
||||||
event.latitude(),
|
|
||||||
event.longitude(),
|
|
||||||
event.odometerKm(),
|
|
||||||
event.rawRecordPath()
|
|
||||||
))
|
|
||||||
.sorted(Comparator.comparing(TachographEsperSupportGeoEvent::occurredAt)
|
.sorted(Comparator.comparing(TachographEsperSupportGeoEvent::occurredAt)
|
||||||
.thenComparing(TachographEsperSupportGeoEvent::eventDomain, Comparator.nullsLast(String::compareTo))
|
.thenComparing(TachographEsperSupportGeoEvent::eventDomain, Comparator.nullsLast(String::compareTo))
|
||||||
.thenComparing(TachographEsperSupportGeoEvent::eventId, Comparator.nullsLast(String::compareTo)))
|
.thenComparing(TachographEsperSupportGeoEvent::eventId, Comparator.nullsLast(String::compareTo)))
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import at.procon.eventhub.processing.dto.UnifiedRuntimeProcessingApiRequest;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeDerivedProjectionEngine;
|
||||||
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.RuntimeSupportEvidenceEvent;
|
||||||
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceNormalizer;
|
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceNormalizer;
|
||||||
|
|
@ -66,7 +67,7 @@ public class UnifiedRuntimeDerivedProjectionService {
|
||||||
driverTimelineBuilder,
|
driverTimelineBuilder,
|
||||||
reusableProjectionBuilder,
|
reusableProjectionBuilder,
|
||||||
properties,
|
properties,
|
||||||
new DriverWorkingTimeProcessingCore(driverTimelineBuilder, reusableProjectionBuilder, properties),
|
new DriverWorkingTimeProcessingCore(new DriverWorkingTimeDerivedProjectionEngine(reusableProjectionBuilder)),
|
||||||
supportEvidenceNormalizer
|
supportEvidenceNormalizer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
import at.procon.eventhub.config.EventHubProperties;
|
import at.procon.eventhub.config.EventHubProperties;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
|
import at.procon.eventhub.processing.driverworkingtime.dto.DriverWorkingTimeProcessingResultDto;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeDerivedProjectionEngine;
|
||||||
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeProcessingCore;
|
import at.procon.eventhub.processing.driverworkingtime.service.DriverWorkingTimeProcessingCore;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographEsperDriverProcessingResultDto;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -27,14 +28,16 @@ public class TachographEsperProcessingCore {
|
||||||
DriverTimelineReusableProjectionBuilder reusableProjectionBuilder,
|
DriverTimelineReusableProjectionBuilder reusableProjectionBuilder,
|
||||||
EventHubProperties properties
|
EventHubProperties properties
|
||||||
) {
|
) {
|
||||||
this(new DriverWorkingTimeProcessingCore(driverTimelineBuilder, reusableProjectionBuilder, properties));
|
this(new DriverWorkingTimeProcessingCore(new DriverWorkingTimeDerivedProjectionEngine(reusableProjectionBuilder)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TachographEsperDriverProcessingResultDto process(TachographEsperProcessingInput input) {
|
public TachographEsperDriverProcessingResultDto process(TachographEsperProcessingInput input) {
|
||||||
return TachographEsperDriverProcessingResultDto.fromDriverWorkingTime(delegate.process(input));
|
return TachographEsperDriverProcessingResultDto.fromDriverWorkingTime(
|
||||||
|
delegate.process(TachographEsperProcessingInputAdapter.toDriverWorkingTimeInput(input))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DriverWorkingTimeProcessingResultDto processDriverWorkingTime(TachographEsperProcessingInput input) {
|
public DriverWorkingTimeProcessingResultDto processDriverWorkingTime(TachographEsperProcessingInput input) {
|
||||||
return delegate.process(input);
|
return delegate.process(TachographEsperProcessingInputAdapter.toDriverWorkingTimeInput(input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeActivityInterval;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeProcessingInput;
|
||||||
|
import at.procon.eventhub.processing.driverworkingtime.model.DriverWorkingTimeVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.processing.eventprocessing.support.RuntimeSupportEvidenceEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class TachographEsperProcessingInputAdapter {
|
||||||
|
|
||||||
|
private TachographEsperProcessingInputAdapter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static DriverWorkingTimeProcessingInput toDriverWorkingTimeInput(TachographEsperProcessingInput input) {
|
||||||
|
ResolvedDriverTimeline timeline = input.timeline();
|
||||||
|
String driverKey = input.driverKey();
|
||||||
|
return new DriverWorkingTimeProcessingInput(
|
||||||
|
input.sessionId(),
|
||||||
|
driverKey,
|
||||||
|
timeline.sourceKind(),
|
||||||
|
timeline.loadedFrom(),
|
||||||
|
timeline.loadedTo(),
|
||||||
|
input.requestedFrom(),
|
||||||
|
input.requestedTo(),
|
||||||
|
input.significantDrivingMinutes(),
|
||||||
|
input.minimumRestPeriodMinutes(),
|
||||||
|
timeline.activityIntervals().stream()
|
||||||
|
.map(interval -> DriverWorkingTimeActivityInterval.fromResolved(input.sessionId(), driverKey, interval))
|
||||||
|
.toList(),
|
||||||
|
timeline.vehicleUsageIntervals().stream()
|
||||||
|
.map(DriverWorkingTimeVehicleUsageInterval::fromResolved)
|
||||||
|
.toList(),
|
||||||
|
timeline.supportEvents().stream()
|
||||||
|
.map(TachographEsperProcessingInputAdapter::toSupportEvidenceEvent)
|
||||||
|
.toList(),
|
||||||
|
input.notes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static 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())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue