Add runtime processing API and rest coverage projections
This commit is contained in:
parent
1ef4ba96bd
commit
9b86d13001
|
|
@ -458,6 +458,179 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Runtime processing",
|
||||||
|
"item": [
|
||||||
|
{
|
||||||
|
"name": "Load runtime driver events from tachograph file session",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"sessionId\": \"{{sessionId}}\",\n \"sourceFamilies\": [\"TACHOGRAPH_FILE_SESSION\"],\n \"driverKey\": \"{{driverKey}}\",\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 0\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/driver-events",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"eventhub",
|
||||||
|
"runtime-processing",
|
||||||
|
"driver-events"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Load runtime driver timeline from tachograph file session",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"sessionId\": \"{{sessionId}}\",\n \"sourceFamilies\": [\"TACHOGRAPH_FILE_SESSION\"],\n \"driverKey\": \"{{driverKey}}\",\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 0\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/driver-timeline",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"eventhub",
|
||||||
|
"runtime-processing",
|
||||||
|
"driver-timeline"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Load runtime driver events from source DBs",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"tenantKey\": \"{{tenantKey}}\",\n \"sourceFamilies\": [\"TACHOGRAPH_DB\", \"YELLOWFOX_DB\"],\n \"eventBackend\": \"SOURCE_DB\",\n \"driverSourceEntityId\": \"{{runtimeDriverSourceEntityId}}\",\n \"driverCardNation\": \"{{runtimeDriverCardNation}}\",\n \"driverCardNumber\": \"{{runtimeDriverCardNumber}}\",\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 30\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/driver-events",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"eventhub",
|
||||||
|
"runtime-processing",
|
||||||
|
"driver-events"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Load runtime driver timeline from source DBs",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"tenantKey\": \"{{tenantKey}}\",\n \"sourceFamilies\": [\"TACHOGRAPH_DB\", \"YELLOWFOX_DB\"],\n \"eventBackend\": \"SOURCE_DB\",\n \"driverSourceEntityId\": \"{{runtimeDriverSourceEntityId}}\",\n \"driverCardNation\": \"{{runtimeDriverCardNation}}\",\n \"driverCardNumber\": \"{{runtimeDriverCardNumber}}\",\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 30\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/driver-timeline",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"eventhub",
|
||||||
|
"runtime-processing",
|
||||||
|
"driver-timeline"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Load runtime driver events from local EventHub DB",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"tenantKey\": \"{{tenantKey}}\",\n \"sourceFamilies\": [\"TACHOGRAPH_DB\", \"YELLOWFOX_DB\"],\n \"eventBackend\": \"EVENTHUB_DB\",\n \"driverSourceEntityId\": \"{{runtimeDriverSourceEntityId}}\",\n \"driverCardNation\": \"{{runtimeDriverCardNation}}\",\n \"driverCardNumber\": \"{{runtimeDriverCardNumber}}\",\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 30\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/driver-events",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"eventhub",
|
||||||
|
"runtime-processing",
|
||||||
|
"driver-events"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Load runtime driver timeline from local EventHub DB",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [
|
||||||
|
{
|
||||||
|
"key": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\n \"tenantKey\": \"{{tenantKey}}\",\n \"sourceFamilies\": [\"TACHOGRAPH_DB\", \"YELLOWFOX_DB\"],\n \"eventBackend\": \"EVENTHUB_DB\",\n \"driverSourceEntityId\": \"{{runtimeDriverSourceEntityId}}\",\n \"driverCardNation\": \"{{runtimeDriverCardNation}}\",\n \"driverCardNumber\": \"{{runtimeDriverCardNumber}}\",\n \"occurredFrom\": \"{{occurredFrom}}\",\n \"occurredTo\": \"{{occurredTo}}\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 30\n}"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/driver-timeline",
|
||||||
|
"host": [
|
||||||
|
"{{baseUrl}}"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"api",
|
||||||
|
"eventhub",
|
||||||
|
"runtime-processing",
|
||||||
|
"driver-timeline"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"variable": [
|
"variable": [
|
||||||
|
|
@ -501,6 +674,18 @@
|
||||||
"key": "driverKey",
|
"key": "driverKey",
|
||||||
"value": "12:12345678901200"
|
"value": "12:12345678901200"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "runtimeDriverSourceEntityId",
|
||||||
|
"value": "DRIVER:42"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "runtimeDriverCardNation",
|
||||||
|
"value": "12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "runtimeDriverCardNumber",
|
||||||
|
"value": "12345678901200"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "tachographDddFile",
|
"key": "tachographDddFile",
|
||||||
"value": "C:\\\\temp\\\\driver-card.ddd"
|
"value": "C:\\\\temp\\\\driver-card.ddd"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package at.procon.eventhub.processing.api;
|
||||||
|
|
||||||
|
import at.procon.eventhub.processing.dto.UnifiedRuntimeProcessingApiRequest;
|
||||||
|
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBundle;
|
||||||
|
import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService;
|
||||||
|
import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/eventhub/runtime-processing")
|
||||||
|
public class UnifiedRuntimeProcessingController {
|
||||||
|
|
||||||
|
private final UnifiedRuntimeEventAssemblyService eventAssemblyService;
|
||||||
|
private final UnifiedRuntimeDriverTimelineService runtimeDriverTimelineService;
|
||||||
|
|
||||||
|
public UnifiedRuntimeProcessingController(
|
||||||
|
UnifiedRuntimeEventAssemblyService eventAssemblyService,
|
||||||
|
UnifiedRuntimeDriverTimelineService runtimeDriverTimelineService
|
||||||
|
) {
|
||||||
|
this.eventAssemblyService = eventAssemblyService;
|
||||||
|
this.runtimeDriverTimelineService = runtimeDriverTimelineService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/driver-events")
|
||||||
|
public ResponseEntity<UnifiedRuntimeEventBundle> loadDriverEvents(
|
||||||
|
@RequestBody UnifiedRuntimeProcessingApiRequest request
|
||||||
|
) {
|
||||||
|
return ResponseEntity.ok(eventAssemblyService.assembleDriverScopedEvents(request.toRuntimeRequest()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/driver-timeline")
|
||||||
|
public ResponseEntity<ResolvedDriverTimeline> loadDriverTimeline(
|
||||||
|
@RequestBody UnifiedRuntimeProcessingApiRequest request
|
||||||
|
) {
|
||||||
|
return ResponseEntity.ok(runtimeDriverTimelineService.loadDriverTimeline(request.toRuntimeRequest()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package at.procon.eventhub.processing.api;
|
||||||
|
|
||||||
|
import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInSessionException;
|
||||||
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@RestControllerAdvice(basePackageClasses = UnifiedRuntimeProcessingController.class)
|
||||||
|
public class UnifiedRuntimeProcessingExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler({
|
||||||
|
TachographFileSessionNotFoundException.class,
|
||||||
|
DriverNotFoundInSessionException.class
|
||||||
|
})
|
||||||
|
public ResponseEntity<Map<String, Object>> notFound(RuntimeException exception) {
|
||||||
|
return error(HttpStatus.NOT_FOUND, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(IllegalArgumentException.class)
|
||||||
|
public ResponseEntity<Map<String, Object>> badRequest(RuntimeException exception) {
|
||||||
|
return error(HttpStatus.BAD_REQUEST, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<Map<String, Object>> error(HttpStatus status, RuntimeException exception) {
|
||||||
|
return ResponseEntity.status(status).body(Map.of(
|
||||||
|
"timestamp", OffsetDateTime.now().toString(),
|
||||||
|
"status", status.value(),
|
||||||
|
"error", status.getReasonPhrase(),
|
||||||
|
"message", exception.getMessage()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
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 java.time.OffsetDateTime;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record UnifiedRuntimeProcessingApiRequest(
|
||||||
|
UUID sessionId,
|
||||||
|
String tenantKey,
|
||||||
|
Set<UnifiedEventSourceFamily> sourceFamilies,
|
||||||
|
UnifiedRuntimeEventBackend eventBackend,
|
||||||
|
String driverKey,
|
||||||
|
String driverSourceEntityId,
|
||||||
|
String driverCardNation,
|
||||||
|
String driverCardNumber,
|
||||||
|
OffsetDateTime occurredFrom,
|
||||||
|
OffsetDateTime occurredTo,
|
||||||
|
Boolean expandVehicleEvents,
|
||||||
|
Integer vehicleExpansionPaddingMinutes
|
||||||
|
) {
|
||||||
|
public UnifiedRuntimeProcessingRequest toRuntimeRequest() {
|
||||||
|
return new UnifiedRuntimeProcessingRequest(
|
||||||
|
sessionId,
|
||||||
|
tenantKey,
|
||||||
|
sourceFamilies,
|
||||||
|
eventBackend,
|
||||||
|
driverKey,
|
||||||
|
driverSourceEntityId,
|
||||||
|
driverCardNation,
|
||||||
|
driverCardNumber,
|
||||||
|
occurredFrom,
|
||||||
|
occurredTo,
|
||||||
|
expandVehicleEvents == null || expandVehicleEvents,
|
||||||
|
vehicleExpansionPaddingMinutes == null ? 0 : Math.max(0, vehicleExpansionPaddingMinutes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package at.procon.eventhub.tachographfilesession.dto;
|
package at.procon.eventhub.tachographfilesession.dto;
|
||||||
|
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
@ -22,7 +24,10 @@ public record TachographEsperDriverProcessingResultDto(
|
||||||
int drivingInterruptionIntervalCount,
|
int drivingInterruptionIntervalCount,
|
||||||
int drivingInterruptionVehicleChangeIntervalCount,
|
int drivingInterruptionVehicleChangeIntervalCount,
|
||||||
int dailyWeeklyRestCandidateIntervalCount,
|
int dailyWeeklyRestCandidateIntervalCount,
|
||||||
|
int dailyWeeklyRestCandidateCoverageIntervalCount,
|
||||||
|
int unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount,
|
||||||
int potentialHomeOvernightStayIntervalCount,
|
int potentialHomeOvernightStayIntervalCount,
|
||||||
|
int potentialInVehicleOvernightStayIntervalCount,
|
||||||
int vehicleUsageIntervalCount,
|
int vehicleUsageIntervalCount,
|
||||||
int vuCardAbsentIntervalCount,
|
int vuCardAbsentIntervalCount,
|
||||||
List<TachographEsperActivityIntervalEvent> activityIntervals,
|
List<TachographEsperActivityIntervalEvent> activityIntervals,
|
||||||
|
|
@ -30,7 +35,10 @@ public record TachographEsperDriverProcessingResultDto(
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals,
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals,
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals,
|
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals,
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> dailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> unclassifiedDailyWeeklyRestCandidateCoverageIntervals,
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals,
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals,
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> potentialInVehicleOvernightStayIntervals,
|
||||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
||||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||||
List<String> notes
|
List<String> notes
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime startedAt,
|
||||||
|
OffsetDateTime endedAt,
|
||||||
|
long durationSeconds,
|
||||||
|
long cardPresentDurationSeconds,
|
||||||
|
double cardPresentCoveragePercent,
|
||||||
|
long unknownDurationSeconds,
|
||||||
|
double unknownCoveragePercent,
|
||||||
|
String previousDrivingSourceIntervalId,
|
||||||
|
String nextDrivingSourceIntervalId,
|
||||||
|
String previousRegistrationKey,
|
||||||
|
String nextRegistrationKey,
|
||||||
|
String previousVehicleKey,
|
||||||
|
String nextVehicleKey
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,11 @@ import java.util.List;
|
||||||
public record TachographEsperDrivingDerivedProjectionBundle(
|
public record TachographEsperDrivingDerivedProjectionBundle(
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals,
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals,
|
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals,
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> dailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> unclassifiedDailyWeeklyRestCandidateCoverageIntervals,
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals,
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals,
|
||||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals,
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals,
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> potentialInVehicleOvernightStayIntervals
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.model;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
||||||
|
UUID sessionId,
|
||||||
|
String driverKey,
|
||||||
|
OffsetDateTime startedAt,
|
||||||
|
OffsetDateTime endedAt,
|
||||||
|
long durationSeconds,
|
||||||
|
long cardPresentDurationSeconds,
|
||||||
|
double cardPresentCoveragePercent,
|
||||||
|
String previousDrivingSourceIntervalId,
|
||||||
|
String nextDrivingSourceIntervalId,
|
||||||
|
String previousRegistrationKey,
|
||||||
|
String nextRegistrationKey,
|
||||||
|
String previousVehicleKey,
|
||||||
|
String nextVehicleKey
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -14,9 +14,11 @@ import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -100,9 +102,12 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
|
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals = new ArrayList<>();
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionIntervals = new ArrayList<>();
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals = new ArrayList<>();
|
List<TachographEsperDrivingInterruptionIntervalEvent> dailyWeeklyRestCandidateIntervals = new ArrayList<>();
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> dailyWeeklyRestCandidateCoverageIntervals = new ArrayList<>();
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> unclassifiedDailyWeeklyRestCandidateCoverageIntervals = new ArrayList<>();
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals = new ArrayList<>();
|
List<TachographEsperDrivingInterruptionIntervalEvent> drivingInterruptionVehicleChangeIntervals = new ArrayList<>();
|
||||||
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals = new ArrayList<>();
|
List<TachographEsperVuCardAbsentIntervalEvent> vuCardAbsentIntervals = new ArrayList<>();
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals = new ArrayList<>();
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> potentialHomeOvernightStayIntervals = new ArrayList<>();
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> potentialInVehicleOvernightStayIntervals = new ArrayList<>();
|
||||||
|
|
||||||
executeWithRuntime(
|
executeWithRuntime(
|
||||||
configuration -> {
|
configuration -> {
|
||||||
|
|
@ -119,9 +124,12 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
Map.of(
|
Map.of(
|
||||||
"drivingInterruptionIntervals", newData -> collectDrivingInterruptionIntervalEvents(newData, drivingInterruptionIntervals),
|
"drivingInterruptionIntervals", newData -> collectDrivingInterruptionIntervalEvents(newData, drivingInterruptionIntervals),
|
||||||
"dailyWeeklyRestCandidateIntervals", newData -> collectDrivingInterruptionIntervalEvents(newData, dailyWeeklyRestCandidateIntervals),
|
"dailyWeeklyRestCandidateIntervals", newData -> collectDrivingInterruptionIntervalEvents(newData, dailyWeeklyRestCandidateIntervals),
|
||||||
|
"dailyWeeklyRestCandidateCoverageIntervals", newData -> collectDailyWeeklyRestCandidateCoverageIntervalEvents(newData, dailyWeeklyRestCandidateCoverageIntervals),
|
||||||
|
"unclassifiedDailyWeeklyRestCandidateCoverageIntervals", newData -> collectDailyWeeklyRestCandidateCoverageIntervalEvents(newData, unclassifiedDailyWeeklyRestCandidateCoverageIntervals),
|
||||||
"drivingInterruptionVehicleChangeIntervals", newData -> collectDrivingInterruptionIntervalEvents(newData, drivingInterruptionVehicleChangeIntervals),
|
"drivingInterruptionVehicleChangeIntervals", newData -> collectDrivingInterruptionIntervalEvents(newData, drivingInterruptionVehicleChangeIntervals),
|
||||||
"vuCardAbsentIntervals", newData -> collectVuCardAbsentIntervalEvents(newData, vuCardAbsentIntervals),
|
"vuCardAbsentIntervals", newData -> collectVuCardAbsentIntervalEvents(newData, vuCardAbsentIntervals),
|
||||||
"potentialHomeOvernightStayIntervals", newData -> collectPotentialHomeOvernightStayIntervalEvents(newData, potentialHomeOvernightStayIntervals)
|
"potentialHomeOvernightStayIntervals", newData -> collectPotentialHomeOvernightStayIntervalEvents(newData, potentialHomeOvernightStayIntervals),
|
||||||
|
"potentialInVehicleOvernightStayIntervals", newData -> collectPotentialInVehicleOvernightStayIntervalEvents(newData, potentialInVehicleOvernightStayIntervals)
|
||||||
),
|
),
|
||||||
runtime -> {
|
runtime -> {
|
||||||
if (vehicleUsageIntervals != null) {
|
if (vehicleUsageIntervals != null) {
|
||||||
|
|
@ -146,14 +154,20 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
return new TachographEsperDrivingDerivedProjectionBundle(
|
return new TachographEsperDrivingDerivedProjectionBundle(
|
||||||
sortDrivingInterruptionIntervals(drivingInterruptionIntervals),
|
sortDrivingInterruptionIntervals(drivingInterruptionIntervals),
|
||||||
sortDrivingInterruptionIntervals(dailyWeeklyRestCandidateIntervals),
|
sortDrivingInterruptionIntervals(dailyWeeklyRestCandidateIntervals),
|
||||||
|
sortDailyWeeklyRestCandidateCoverageIntervals(dailyWeeklyRestCandidateCoverageIntervals),
|
||||||
|
sortDailyWeeklyRestCandidateCoverageIntervals(unclassifiedDailyWeeklyRestCandidateCoverageIntervals),
|
||||||
sortDrivingInterruptionIntervals(drivingInterruptionVehicleChangeIntervals),
|
sortDrivingInterruptionIntervals(drivingInterruptionVehicleChangeIntervals),
|
||||||
sortVuCardAbsentIntervals(vuCardAbsentIntervals),
|
sortVuCardAbsentIntervals(vuCardAbsentIntervals),
|
||||||
sortPotentialHomeOvernightStayIntervals(potentialHomeOvernightStayIntervals)
|
sortPotentialHomeOvernightStayIntervals(potentialHomeOvernightStayIntervals),
|
||||||
|
sortPotentialInVehicleOvernightStayIntervals(potentialInVehicleOvernightStayIntervals)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TachographEsperDrivingDerivedProjectionBundle emptyBundle() {
|
private TachographEsperDrivingDerivedProjectionBundle emptyBundle() {
|
||||||
return new TachographEsperDrivingDerivedProjectionBundle(
|
return new TachographEsperDrivingDerivedProjectionBundle(
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of(),
|
List.of(),
|
||||||
List.of(),
|
List.of(),
|
||||||
|
|
@ -364,6 +378,36 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void collectDailyWeeklyRestCandidateCoverageIntervalEvents(
|
||||||
|
EventBean[] newData,
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> target
|
||||||
|
) {
|
||||||
|
if (newData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (EventBean event : newData) {
|
||||||
|
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||||
|
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||||
|
target.add(new TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||||
|
(UUID) event.get("sessionId"),
|
||||||
|
(String) event.get("driverKey"),
|
||||||
|
OffsetDateTime.ofInstant(Instant.ofEpochSecond(startedAtEpochSecond), ZoneOffset.UTC),
|
||||||
|
OffsetDateTime.ofInstant(Instant.ofEpochSecond(endedAtEpochSecond), ZoneOffset.UTC),
|
||||||
|
(Long) event.get("durationSeconds"),
|
||||||
|
(Long) event.get("cardPresentDurationSeconds"),
|
||||||
|
(Double) event.get("cardPresentCoveragePercent"),
|
||||||
|
(Long) event.get("unknownDurationSeconds"),
|
||||||
|
(Double) event.get("unknownCoveragePercent"),
|
||||||
|
(String) event.get("previousDrivingSourceIntervalId"),
|
||||||
|
(String) event.get("nextDrivingSourceIntervalId"),
|
||||||
|
(String) event.get("previousRegistrationKey"),
|
||||||
|
(String) event.get("nextRegistrationKey"),
|
||||||
|
(String) event.get("previousVehicleKey"),
|
||||||
|
(String) event.get("nextVehicleKey")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void collectPotentialHomeOvernightStayIntervalEvents(
|
private void collectPotentialHomeOvernightStayIntervalEvents(
|
||||||
EventBean[] newData,
|
EventBean[] newData,
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> target
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> target
|
||||||
|
|
@ -392,6 +436,34 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void collectPotentialInVehicleOvernightStayIntervalEvents(
|
||||||
|
EventBean[] newData,
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> target
|
||||||
|
) {
|
||||||
|
if (newData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (EventBean event : newData) {
|
||||||
|
long startedAtEpochSecond = (Long) event.get("startedAtEpochSecond");
|
||||||
|
long endedAtEpochSecond = (Long) event.get("endedAtEpochSecond");
|
||||||
|
target.add(new TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
||||||
|
(UUID) event.get("sessionId"),
|
||||||
|
(String) event.get("driverKey"),
|
||||||
|
OffsetDateTime.ofInstant(Instant.ofEpochSecond(startedAtEpochSecond), ZoneOffset.UTC),
|
||||||
|
OffsetDateTime.ofInstant(Instant.ofEpochSecond(endedAtEpochSecond), ZoneOffset.UTC),
|
||||||
|
(Long) event.get("durationSeconds"),
|
||||||
|
(Long) event.get("cardPresentDurationSeconds"),
|
||||||
|
(Double) event.get("cardPresentCoveragePercent"),
|
||||||
|
(String) event.get("previousDrivingSourceIntervalId"),
|
||||||
|
(String) event.get("nextDrivingSourceIntervalId"),
|
||||||
|
(String) event.get("previousRegistrationKey"),
|
||||||
|
(String) event.get("nextRegistrationKey"),
|
||||||
|
(String) event.get("previousVehicleKey"),
|
||||||
|
(String) event.get("nextVehicleKey")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<TachographEsperDrivingInterruptionIntervalEvent> sortDrivingInterruptionIntervals(
|
private List<TachographEsperDrivingInterruptionIntervalEvent> sortDrivingInterruptionIntervals(
|
||||||
List<TachographEsperDrivingInterruptionIntervalEvent> intervals
|
List<TachographEsperDrivingInterruptionIntervalEvent> intervals
|
||||||
) {
|
) {
|
||||||
|
|
@ -410,6 +482,15 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> sortDailyWeeklyRestCandidateCoverageIntervals(
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> intervals
|
||||||
|
) {
|
||||||
|
return intervals.stream()
|
||||||
|
.sorted(Comparator.comparing(TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent::startedAt)
|
||||||
|
.thenComparing(TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent::endedAt))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
private List<TachographEsperPotentialHomeOvernightStayIntervalEvent> sortPotentialHomeOvernightStayIntervals(
|
private List<TachographEsperPotentialHomeOvernightStayIntervalEvent> sortPotentialHomeOvernightStayIntervals(
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals
|
||||||
) {
|
) {
|
||||||
|
|
@ -419,6 +500,15 @@ public class DriverTimelineReusableProjectionBuilder {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> sortPotentialInVehicleOvernightStayIntervals(
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> intervals
|
||||||
|
) {
|
||||||
|
return intervals.stream()
|
||||||
|
.sorted(Comparator.comparing(TachographEsperPotentialInVehicleOvernightStayIntervalEvent::startedAt)
|
||||||
|
.thenComparing(TachographEsperPotentialInVehicleOvernightStayIntervalEvent::endedAt))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
private String renderDrivingDerivedProjectionBundleEpl(int significantDrivingMinutes, int minimumRestPeriodMinutes) {
|
private String renderDrivingDerivedProjectionBundleEpl(int significantDrivingMinutes, int minimumRestPeriodMinutes) {
|
||||||
return DRIVING_DERIVED_PROJECTION_BUNDLE_EPL_TEMPLATE
|
return DRIVING_DERIVED_PROJECTION_BUNDLE_EPL_TEMPLATE
|
||||||
.replace(
|
.replace(
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,9 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE
|
||||||
eventSource,
|
eventSource,
|
||||||
sourcePackageRef
|
sourcePackageRef
|
||||||
);
|
);
|
||||||
List<EventHubEventDto> supportEvents = buildSupportEvents(
|
List<EventHubEventDto> supportEvents = List.of();
|
||||||
|
/*
|
||||||
|
buildSupportEvents(
|
||||||
session,
|
session,
|
||||||
timeline.supportEvents(),
|
timeline.supportEvents(),
|
||||||
driverRef,
|
driverRef,
|
||||||
|
|
@ -120,6 +122,7 @@ public class IntervalBackedDriverTimelineEventBuilder implements DriverTimelineE
|
||||||
eventSource,
|
eventSource,
|
||||||
sourcePackageRef
|
sourcePackageRef
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
return new TachographTimelineEventBundle(activityEvents, vehicleUsageEvents, supportEvents);
|
return new TachographTimelineEventBundle(activityEvents, vehicleUsageEvents, supportEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,12 @@ import at.procon.eventhub.tachographfilesession.model.ProcessedShiftDrivingEvalu
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialInVehicleOvernightStayIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVuCardAbsentIntervalEvent;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
@ -209,8 +211,33 @@ public class TachographFileSessionProcessingService {
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo
|
requestedTo
|
||||||
);
|
);
|
||||||
|
List<TachographEsperVehicleUsageIntervalEvent> rawVehicleUsageIntervals =
|
||||||
|
driverTimelineBuilder.buildEsperVehicleUsageIntervalEvents(timeline);
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> dailyWeeklyRestCandidateCoverageIntervals =
|
||||||
|
clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
||||||
|
derivedProjectionBundle.dailyWeeklyRestCandidateCoverageIntervals(),
|
||||||
|
rawVuCardAbsentIntervals,
|
||||||
|
rawVehicleUsageIntervals,
|
||||||
|
requestedFrom,
|
||||||
|
requestedTo
|
||||||
|
);
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> unclassifiedDailyWeeklyRestCandidateCoverageIntervals =
|
||||||
|
clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
||||||
|
derivedProjectionBundle.unclassifiedDailyWeeklyRestCandidateCoverageIntervals(),
|
||||||
|
rawVuCardAbsentIntervals,
|
||||||
|
rawVehicleUsageIntervals,
|
||||||
|
requestedFrom,
|
||||||
|
requestedTo
|
||||||
|
);
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> potentialInVehicleOvernightStayIntervals =
|
||||||
|
clipEsperPotentialInVehicleOvernightStayIntervalEvents(
|
||||||
|
derivedProjectionBundle.potentialInVehicleOvernightStayIntervals(),
|
||||||
|
rawVehicleUsageIntervals,
|
||||||
|
requestedFrom,
|
||||||
|
requestedTo
|
||||||
|
);
|
||||||
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals = clipEsperVehicleUsageIntervalEvents(
|
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals = clipEsperVehicleUsageIntervalEvents(
|
||||||
driverTimelineBuilder.buildEsperVehicleUsageIntervalEvents(timeline),
|
rawVehicleUsageIntervals,
|
||||||
requestedFrom,
|
requestedFrom,
|
||||||
requestedTo
|
requestedTo
|
||||||
);
|
);
|
||||||
|
|
@ -233,7 +260,10 @@ public class TachographFileSessionProcessingService {
|
||||||
drivingInterruptionIntervals.size(),
|
drivingInterruptionIntervals.size(),
|
||||||
drivingInterruptionVehicleChangeIntervals.size(),
|
drivingInterruptionVehicleChangeIntervals.size(),
|
||||||
dailyWeeklyRestCandidateIntervals.size(),
|
dailyWeeklyRestCandidateIntervals.size(),
|
||||||
|
dailyWeeklyRestCandidateCoverageIntervals.size(),
|
||||||
|
unclassifiedDailyWeeklyRestCandidateCoverageIntervals.size(),
|
||||||
potentialHomeOvernightStayIntervals.size(),
|
potentialHomeOvernightStayIntervals.size(),
|
||||||
|
potentialInVehicleOvernightStayIntervals.size(),
|
||||||
vehicleUsageIntervals.size(),
|
vehicleUsageIntervals.size(),
|
||||||
vuCardAbsentIntervals.size(),
|
vuCardAbsentIntervals.size(),
|
||||||
activityIntervals,
|
activityIntervals,
|
||||||
|
|
@ -241,7 +271,10 @@ public class TachographFileSessionProcessingService {
|
||||||
drivingInterruptionIntervals,
|
drivingInterruptionIntervals,
|
||||||
drivingInterruptionVehicleChangeIntervals,
|
drivingInterruptionVehicleChangeIntervals,
|
||||||
dailyWeeklyRestCandidateIntervals,
|
dailyWeeklyRestCandidateIntervals,
|
||||||
|
dailyWeeklyRestCandidateCoverageIntervals,
|
||||||
|
unclassifiedDailyWeeklyRestCandidateCoverageIntervals,
|
||||||
potentialHomeOvernightStayIntervals,
|
potentialHomeOvernightStayIntervals,
|
||||||
|
potentialInVehicleOvernightStayIntervals,
|
||||||
vehicleUsageIntervals,
|
vehicleUsageIntervals,
|
||||||
vuCardAbsentIntervals,
|
vuCardAbsentIntervals,
|
||||||
esperProjectionNotes()
|
esperProjectionNotes()
|
||||||
|
|
@ -413,6 +446,67 @@ public class TachographFileSessionProcessingService {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> clipEsperDailyWeeklyRestCandidateCoverageIntervalEvents(
|
||||||
|
List<TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent> intervals,
|
||||||
|
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals,
|
||||||
|
List<TachographEsperVehicleUsageIntervalEvent> rawVehicleUsageIntervals,
|
||||||
|
OffsetDateTime requestedFrom,
|
||||||
|
OffsetDateTime requestedTo
|
||||||
|
) {
|
||||||
|
if (requestedFrom == null || requestedTo == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return intervals.stream()
|
||||||
|
.map(interval -> {
|
||||||
|
OffsetDateTime start = max(interval.startedAt(), requestedFrom);
|
||||||
|
OffsetDateTime end = min(interval.endedAt(), requestedTo);
|
||||||
|
if (!end.isAfter(start)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long durationSeconds = Duration.between(start, end).getSeconds();
|
||||||
|
long cardPresentDurationSeconds = overlapVehicleUsageSeconds(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
rawVehicleUsageIntervals,
|
||||||
|
interval.driverKey(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
double cardPresentCoveragePercent = durationSeconds == 0L
|
||||||
|
? 0.0d
|
||||||
|
: (cardPresentDurationSeconds * 100.0d) / durationSeconds;
|
||||||
|
long unknownDurationSeconds = overlapSeconds(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
rawVuCardAbsentIntervals,
|
||||||
|
interval.driverKey()
|
||||||
|
);
|
||||||
|
double unknownCoveragePercent = durationSeconds == 0L
|
||||||
|
? 0.0d
|
||||||
|
: (unknownDurationSeconds * 100.0d) / durationSeconds;
|
||||||
|
return new TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||||
|
interval.sessionId(),
|
||||||
|
interval.driverKey(),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
durationSeconds,
|
||||||
|
cardPresentDurationSeconds,
|
||||||
|
cardPresentCoveragePercent,
|
||||||
|
unknownDurationSeconds,
|
||||||
|
unknownCoveragePercent,
|
||||||
|
interval.previousDrivingSourceIntervalId(),
|
||||||
|
interval.nextDrivingSourceIntervalId(),
|
||||||
|
interval.previousRegistrationKey(),
|
||||||
|
interval.nextRegistrationKey(),
|
||||||
|
interval.previousVehicleKey(),
|
||||||
|
interval.nextVehicleKey()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.sorted(Comparator.comparing(TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent::startedAt)
|
||||||
|
.thenComparing(TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent::endedAt))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
private List<TachographEsperPotentialHomeOvernightStayIntervalEvent> clipEsperPotentialHomeOvernightStayIntervalEvents(
|
private List<TachographEsperPotentialHomeOvernightStayIntervalEvent> clipEsperPotentialHomeOvernightStayIntervalEvents(
|
||||||
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals,
|
List<TachographEsperPotentialHomeOvernightStayIntervalEvent> intervals,
|
||||||
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals,
|
List<TachographEsperVuCardAbsentIntervalEvent> rawVuCardAbsentIntervals,
|
||||||
|
|
@ -461,6 +555,55 @@ public class TachographFileSessionProcessingService {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> clipEsperPotentialInVehicleOvernightStayIntervalEvents(
|
||||||
|
List<TachographEsperPotentialInVehicleOvernightStayIntervalEvent> intervals,
|
||||||
|
List<TachographEsperVehicleUsageIntervalEvent> rawVehicleUsageIntervals,
|
||||||
|
OffsetDateTime requestedFrom,
|
||||||
|
OffsetDateTime requestedTo
|
||||||
|
) {
|
||||||
|
if (requestedFrom == null || requestedTo == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return intervals.stream()
|
||||||
|
.map(interval -> {
|
||||||
|
OffsetDateTime start = max(interval.startedAt(), requestedFrom);
|
||||||
|
OffsetDateTime end = min(interval.endedAt(), requestedTo);
|
||||||
|
if (!end.isAfter(start)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long durationSeconds = Duration.between(start, end).getSeconds();
|
||||||
|
long cardPresentDurationSeconds = overlapVehicleUsageSeconds(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
rawVehicleUsageIntervals,
|
||||||
|
interval.driverKey(),
|
||||||
|
interval.previousRegistrationKey()
|
||||||
|
);
|
||||||
|
double cardPresentCoveragePercent = durationSeconds == 0L
|
||||||
|
? 0.0d
|
||||||
|
: (cardPresentDurationSeconds * 100.0d) / durationSeconds;
|
||||||
|
return new TachographEsperPotentialInVehicleOvernightStayIntervalEvent(
|
||||||
|
interval.sessionId(),
|
||||||
|
interval.driverKey(),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
durationSeconds,
|
||||||
|
cardPresentDurationSeconds,
|
||||||
|
cardPresentCoveragePercent,
|
||||||
|
interval.previousDrivingSourceIntervalId(),
|
||||||
|
interval.nextDrivingSourceIntervalId(),
|
||||||
|
interval.previousRegistrationKey(),
|
||||||
|
interval.nextRegistrationKey(),
|
||||||
|
interval.previousVehicleKey(),
|
||||||
|
interval.nextVehicleKey()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.sorted(Comparator.comparing(TachographEsperPotentialInVehicleOvernightStayIntervalEvent::startedAt)
|
||||||
|
.thenComparing(TachographEsperPotentialInVehicleOvernightStayIntervalEvent::endedAt))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
private List<ResolvedActivityInterval> synthesizeUnknownGaps(
|
private List<ResolvedActivityInterval> synthesizeUnknownGaps(
|
||||||
List<ResolvedActivityInterval> knownIntervals,
|
List<ResolvedActivityInterval> knownIntervals,
|
||||||
Duration gapDetectionTolerance
|
Duration gapDetectionTolerance
|
||||||
|
|
@ -1027,7 +1170,10 @@ public class TachographFileSessionProcessingService {
|
||||||
"Driving interruption intervals are gaps between consecutive driving intervals longer than the configured significant-driving threshold.",
|
"Driving interruption intervals are gaps between consecutive driving intervals longer than the configured significant-driving threshold.",
|
||||||
"Driving interruption vehicle-change intervals are daily/weekly rest candidates where previousRegistrationKey differs from nextRegistrationKey.",
|
"Driving interruption vehicle-change intervals are daily/weekly rest candidates where previousRegistrationKey differs from nextRegistrationKey.",
|
||||||
"Daily/weekly rest candidate intervals are driving interruption intervals longer than the configured minimum rest-period threshold.",
|
"Daily/weekly rest candidate intervals are driving interruption intervals longer than the configured minimum rest-period threshold.",
|
||||||
"Potential home overnight stay intervals are vehicle-change daily/weekly rest candidates where VU card-absent overlap covers at least 95% of the candidate interval.",
|
"Daily/weekly rest candidate coverage intervals enrich each rest candidate with card-present and unknown-coverage metrics computed from vehicle-usage and VU card-absent overlap.",
|
||||||
|
"Unclassified daily/weekly rest candidate coverage intervals are the rest candidates that are neither potential home overnight stays nor potential in-vehicle overnight stays.",
|
||||||
|
"Potential home overnight stay intervals are vehicle-change daily/weekly rest candidate coverage intervals where VU card-absent overlap covers at least 95% of the candidate interval.",
|
||||||
|
"Potential in-vehicle overnight stay intervals are no-change daily/weekly rest candidate coverage intervals where card-present overlap covers the candidate rest period.",
|
||||||
"VU card-absent intervals are gaps between consecutive normalized vehicle-usage intervals for the same driver.",
|
"VU card-absent intervals are gaps between consecutive normalized vehicle-usage intervals for the same driver.",
|
||||||
"occurredFrom and occurredTo clip the returned interval projections to the requested UTC time window.",
|
"occurredFrom and occurredTo clip the returned interval projections to the requested UTC time window.",
|
||||||
"Vehicle-usage intervals clear clipped odometer endpoints because boundary odometer values cannot be recomputed safely from the source interval."
|
"Vehicle-usage intervals clear clipped odometer endpoints because boundary odometer values cannot be recomputed safely from the source interval."
|
||||||
|
|
@ -1057,6 +1203,36 @@ public class TachographFileSessionProcessingService {
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long overlapVehicleUsageSeconds(
|
||||||
|
OffsetDateTime intervalStart,
|
||||||
|
OffsetDateTime intervalEnd,
|
||||||
|
List<TachographEsperVehicleUsageIntervalEvent> vehicleUsageIntervals,
|
||||||
|
String driverKey,
|
||||||
|
String registrationKey
|
||||||
|
) {
|
||||||
|
if (vehicleUsageIntervals == null || vehicleUsageIntervals.isEmpty()) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
long total = 0L;
|
||||||
|
for (TachographEsperVehicleUsageIntervalEvent vehicleUsage : vehicleUsageIntervals) {
|
||||||
|
if (!Objects.equals(driverKey, vehicleUsage.driverKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (registrationKey != null && !Objects.equals(registrationKey, vehicleUsage.registrationKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (vehicleUsage.endedAt() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
OffsetDateTime overlapStart = max(intervalStart, vehicleUsage.startedAt());
|
||||||
|
OffsetDateTime overlapEnd = min(intervalEnd, vehicleUsage.endedAt());
|
||||||
|
if (overlapEnd.isAfter(overlapStart)) {
|
||||||
|
total += Duration.between(overlapStart, overlapEnd).getSeconds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
private OffsetDateTime utc(OffsetDateTime value) {
|
private OffsetDateTime utc(OffsetDateTime value) {
|
||||||
return value == null ? null : value.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
return value == null ? null : value.withOffsetSameInstant(java.time.ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,16 @@ import java.util.Comparator;
|
||||||
import java.util.HexFormat;
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class TachographFileSessionService {
|
public class TachographFileSessionService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TachographFileSessionService.class);
|
||||||
|
|
||||||
private final EventHubProperties properties;
|
private final EventHubProperties properties;
|
||||||
private final TachographFileSessionRepository repository;
|
private final TachographFileSessionRepository repository;
|
||||||
private final LegalRequirementsClient legalRequirementsClient;
|
private final LegalRequirementsClient legalRequirementsClient;
|
||||||
|
|
@ -57,8 +61,14 @@ public class TachographFileSessionService {
|
||||||
validateFile(file);
|
validateFile(file);
|
||||||
byte[] fileBytes = file.getBytes();
|
byte[] fileBytes = file.getBytes();
|
||||||
validateFileBytes(fileBytes);
|
validateFileBytes(fileBytes);
|
||||||
|
long uploadStartedAt = System.nanoTime();
|
||||||
LegalRequirementsUploadResult uploadResult = legalRequirementsClient.uploadTachographFile(fileBytes, file.getOriginalFilename());
|
LegalRequirementsUploadResult uploadResult = legalRequirementsClient.uploadTachographFile(fileBytes, file.getOriginalFilename());
|
||||||
|
long uploadDurationMs = elapsedMillis(uploadStartedAt);
|
||||||
|
|
||||||
|
long parseStartedAt = System.nanoTime();
|
||||||
TachographXmlParser.ParsedTachographXml parsedXml = tachographXmlParser.parse(uploadResult.xmlContent());
|
TachographXmlParser.ParsedTachographXml parsedXml = tachographXmlParser.parse(uploadResult.xmlContent());
|
||||||
|
long parseDurationMs = elapsedMillis(parseStartedAt);
|
||||||
|
|
||||||
Instant createdAt = Instant.now();
|
Instant createdAt = Instant.now();
|
||||||
Instant expiresAt = createdAt.plus(properties.getTachographFileSession().getTtl());
|
Instant expiresAt = createdAt.plus(properties.getTachographFileSession().getTtl());
|
||||||
boolean driverCardFile = "DriverCard".equals(parsedXml.rootElementName());
|
boolean driverCardFile = "DriverCard".equals(parsedXml.rootElementName());
|
||||||
|
|
@ -74,9 +84,21 @@ public class TachographFileSessionService {
|
||||||
driverCardFile,
|
driverCardFile,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
long extractStartedAt = System.nanoTime();
|
||||||
TachographFileSession session = driverCardFile
|
TachographFileSession session = driverCardFile
|
||||||
? driverCardExtractionService.extract(parsedXml, metadata, createdAt, expiresAt)
|
? driverCardExtractionService.extract(parsedXml, metadata, createdAt, expiresAt)
|
||||||
: vehicleUnitExtractionService.extract(parsedXml, metadata, createdAt, expiresAt);
|
: vehicleUnitExtractionService.extract(parsedXml, metadata, createdAt, expiresAt);
|
||||||
|
long extractDurationMs = elapsedMillis(extractStartedAt);
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"Created tachograph file session timing: file='{}', type={}, uploadMs={}, parseMs={}, extractMs={}, sizeBytes={}",
|
||||||
|
file.getOriginalFilename(),
|
||||||
|
driverCardFile ? "DRIVER_CARD" : "VEHICLE_UNIT",
|
||||||
|
uploadDurationMs,
|
||||||
|
parseDurationMs,
|
||||||
|
extractDurationMs,
|
||||||
|
fileBytes.length
|
||||||
|
);
|
||||||
TachographFileSession saved = repository.save(session);
|
TachographFileSession saved = repository.save(session);
|
||||||
return new CreateTachographFileSessionResponse(toSummary(saved));
|
return new CreateTachographFileSessionResponse(toSummary(saved));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -196,4 +218,8 @@ public class TachographFileSessionService {
|
||||||
throw new IllegalStateException("SHA-256 digest is not available.", e);
|
throw new IllegalStateException("SHA-256 digest is not available.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long elapsedMillis(long startedAtNanos) {
|
||||||
|
return (System.nanoTime() - startedAtNanos) / 1_000_000L;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,69 @@ create schema DrivingInterruptionVehicleChangeInterval(
|
||||||
nextVehicleKey string
|
nextVehicleKey string
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create schema DrivingInterruptionVehicleNotChangedInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAtEpochSecond long,
|
||||||
|
endedAtEpochSecond long,
|
||||||
|
durationSeconds long,
|
||||||
|
previousDrivingSourceIntervalId string,
|
||||||
|
nextDrivingSourceIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
|
create schema DailyWeeklyRestCandidateCoverageUnknownResolvedInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAtEpochSecond long,
|
||||||
|
endedAtEpochSecond long,
|
||||||
|
durationSeconds long,
|
||||||
|
unknownDurationSeconds long,
|
||||||
|
previousDrivingSourceIntervalId string,
|
||||||
|
nextDrivingSourceIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
|
create schema DailyWeeklyRestCandidateCoverageCardResolvedInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAtEpochSecond long,
|
||||||
|
endedAtEpochSecond long,
|
||||||
|
durationSeconds long,
|
||||||
|
cardPresentDurationSeconds long,
|
||||||
|
unknownDurationSeconds long,
|
||||||
|
previousDrivingSourceIntervalId string,
|
||||||
|
nextDrivingSourceIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
|
create schema DailyWeeklyRestCandidateCoverageInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAtEpochSecond long,
|
||||||
|
endedAtEpochSecond long,
|
||||||
|
durationSeconds long,
|
||||||
|
cardPresentDurationSeconds long,
|
||||||
|
cardPresentCoveragePercent double,
|
||||||
|
unknownDurationSeconds long,
|
||||||
|
unknownCoveragePercent double,
|
||||||
|
previousDrivingSourceIntervalId string,
|
||||||
|
nextDrivingSourceIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
create context PerDriver partition by driverKey from TachographVehicleUsageIntervalInputEvent;
|
create context PerDriver partition by driverKey from TachographVehicleUsageIntervalInputEvent;
|
||||||
|
|
||||||
create schema VuCardAbsentInterval(
|
create schema VuCardAbsentInterval(
|
||||||
|
|
@ -84,6 +147,40 @@ create schema PotentialHomeOvernightStayInterval(
|
||||||
nextVehicleKey string
|
nextVehicleKey string
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create schema PotentialInVehicleOvernightStayInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAtEpochSecond long,
|
||||||
|
endedAtEpochSecond long,
|
||||||
|
durationSeconds long,
|
||||||
|
cardPresentDurationSeconds long,
|
||||||
|
cardPresentCoveragePercent double,
|
||||||
|
previousDrivingSourceIntervalId string,
|
||||||
|
nextDrivingSourceIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
|
create schema UnclassifiedDailyWeeklyRestCandidateCoverageInterval(
|
||||||
|
sessionId java.util.UUID,
|
||||||
|
driverKey string,
|
||||||
|
startedAtEpochSecond long,
|
||||||
|
endedAtEpochSecond long,
|
||||||
|
durationSeconds long,
|
||||||
|
cardPresentDurationSeconds long,
|
||||||
|
cardPresentCoveragePercent double,
|
||||||
|
unknownDurationSeconds long,
|
||||||
|
unknownCoveragePercent double,
|
||||||
|
previousDrivingSourceIntervalId string,
|
||||||
|
nextDrivingSourceIntervalId string,
|
||||||
|
previousRegistrationKey string,
|
||||||
|
nextRegistrationKey string,
|
||||||
|
previousVehicleKey string,
|
||||||
|
nextVehicleKey string
|
||||||
|
);
|
||||||
|
|
||||||
insert into SignificantDrivingInterval
|
insert into SignificantDrivingInterval
|
||||||
select
|
select
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|
@ -138,6 +235,164 @@ from DailyWeeklyRestCandidateInterval(
|
||||||
previousRegistrationKey != nextRegistrationKey
|
previousRegistrationKey != nextRegistrationKey
|
||||||
);
|
);
|
||||||
|
|
||||||
|
insert into DrivingInterruptionVehicleNotChangedInterval
|
||||||
|
select *
|
||||||
|
from DailyWeeklyRestCandidateInterval(
|
||||||
|
previousRegistrationKey is not null,
|
||||||
|
nextRegistrationKey is not null,
|
||||||
|
previousRegistrationKey = nextRegistrationKey
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into DailyWeeklyRestCandidateCoverageUnknownResolvedInterval
|
||||||
|
select
|
||||||
|
c.sessionId as sessionId,
|
||||||
|
c.driverKey as driverKey,
|
||||||
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
|
c.durationSeconds as durationSeconds,
|
||||||
|
sum(
|
||||||
|
case
|
||||||
|
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||||
|
then c.durationSeconds
|
||||||
|
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
||||||
|
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
||||||
|
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||||
|
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
||||||
|
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
||||||
|
end
|
||||||
|
) as unknownDurationSeconds,
|
||||||
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
|
c.nextVehicleKey as nextVehicleKey
|
||||||
|
from DailyWeeklyRestCandidateInterval as c unidirectional,
|
||||||
|
VuCardAbsentInterval#keepall as u
|
||||||
|
where u.driverKey = c.driverKey
|
||||||
|
and u.startedAtEpochSecond < c.endedAtEpochSecond
|
||||||
|
and u.endedAtEpochSecond > c.startedAtEpochSecond
|
||||||
|
group by
|
||||||
|
c.sessionId,
|
||||||
|
c.driverKey,
|
||||||
|
c.startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond,
|
||||||
|
c.durationSeconds,
|
||||||
|
c.previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey,
|
||||||
|
c.previousVehicleKey,
|
||||||
|
c.nextVehicleKey;
|
||||||
|
|
||||||
|
insert into DailyWeeklyRestCandidateCoverageUnknownResolvedInterval
|
||||||
|
select
|
||||||
|
c.sessionId as sessionId,
|
||||||
|
c.driverKey as driverKey,
|
||||||
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
|
c.durationSeconds as durationSeconds,
|
||||||
|
0L as unknownDurationSeconds,
|
||||||
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
|
c.nextVehicleKey as nextVehicleKey
|
||||||
|
from DailyWeeklyRestCandidateInterval as c
|
||||||
|
where not exists (
|
||||||
|
select * from VuCardAbsentInterval#keepall as u
|
||||||
|
where u.driverKey = c.driverKey
|
||||||
|
and u.startedAtEpochSecond < c.endedAtEpochSecond
|
||||||
|
and u.endedAtEpochSecond > c.startedAtEpochSecond
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into DailyWeeklyRestCandidateCoverageCardResolvedInterval
|
||||||
|
select
|
||||||
|
c.sessionId as sessionId,
|
||||||
|
c.driverKey as driverKey,
|
||||||
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
|
c.durationSeconds as durationSeconds,
|
||||||
|
sum(
|
||||||
|
case
|
||||||
|
when v.startedAtEpochSecond <= c.startedAtEpochSecond and (v.endedAtEpochSecond is null or v.endedAtEpochSecond >= c.endedAtEpochSecond)
|
||||||
|
then c.durationSeconds
|
||||||
|
when v.startedAtEpochSecond <= c.startedAtEpochSecond
|
||||||
|
then v.endedAtEpochSecond - c.startedAtEpochSecond
|
||||||
|
when v.endedAtEpochSecond is null or v.endedAtEpochSecond >= c.endedAtEpochSecond
|
||||||
|
then c.endedAtEpochSecond - v.startedAtEpochSecond
|
||||||
|
else v.endedAtEpochSecond - v.startedAtEpochSecond
|
||||||
|
end
|
||||||
|
) as cardPresentDurationSeconds,
|
||||||
|
c.unknownDurationSeconds as unknownDurationSeconds,
|
||||||
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
|
c.nextVehicleKey as nextVehicleKey
|
||||||
|
from DailyWeeklyRestCandidateCoverageUnknownResolvedInterval as c unidirectional,
|
||||||
|
TachographVehicleUsageIntervalInputEvent#keepall as v
|
||||||
|
where v.driverKey = c.driverKey
|
||||||
|
and v.startedAtEpochSecond < c.endedAtEpochSecond
|
||||||
|
and (v.endedAtEpochSecond is null or v.endedAtEpochSecond > c.startedAtEpochSecond)
|
||||||
|
group by
|
||||||
|
c.sessionId,
|
||||||
|
c.driverKey,
|
||||||
|
c.startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond,
|
||||||
|
c.durationSeconds,
|
||||||
|
c.unknownDurationSeconds,
|
||||||
|
c.previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey,
|
||||||
|
c.previousVehicleKey,
|
||||||
|
c.nextVehicleKey;
|
||||||
|
|
||||||
|
insert into DailyWeeklyRestCandidateCoverageCardResolvedInterval
|
||||||
|
select
|
||||||
|
c.sessionId as sessionId,
|
||||||
|
c.driverKey as driverKey,
|
||||||
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
|
c.durationSeconds as durationSeconds,
|
||||||
|
0L as cardPresentDurationSeconds,
|
||||||
|
c.unknownDurationSeconds as unknownDurationSeconds,
|
||||||
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
|
c.nextVehicleKey as nextVehicleKey
|
||||||
|
from DailyWeeklyRestCandidateCoverageUnknownResolvedInterval as c
|
||||||
|
where not exists (
|
||||||
|
select * from TachographVehicleUsageIntervalInputEvent#keepall as v
|
||||||
|
where v.driverKey = c.driverKey
|
||||||
|
and v.startedAtEpochSecond < c.endedAtEpochSecond
|
||||||
|
and (v.endedAtEpochSecond is null or v.endedAtEpochSecond > c.startedAtEpochSecond)
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into DailyWeeklyRestCandidateCoverageInterval
|
||||||
|
select
|
||||||
|
sessionId,
|
||||||
|
driverKey,
|
||||||
|
startedAtEpochSecond,
|
||||||
|
endedAtEpochSecond,
|
||||||
|
durationSeconds,
|
||||||
|
cardPresentDurationSeconds,
|
||||||
|
(cardPresentDurationSeconds * 100.0d) / durationSeconds as cardPresentCoveragePercent,
|
||||||
|
unknownDurationSeconds,
|
||||||
|
(unknownDurationSeconds * 100.0d) / durationSeconds as unknownCoveragePercent,
|
||||||
|
previousDrivingSourceIntervalId,
|
||||||
|
nextDrivingSourceIntervalId,
|
||||||
|
previousRegistrationKey,
|
||||||
|
nextRegistrationKey,
|
||||||
|
previousVehicleKey,
|
||||||
|
nextVehicleKey
|
||||||
|
from DailyWeeklyRestCandidateCoverageCardResolvedInterval;
|
||||||
|
|
||||||
context PerDriver
|
context PerDriver
|
||||||
create window PreviousVehicleUsageInterval#lastevent as TachographVehicleUsageIntervalInputEvent;
|
create window PreviousVehicleUsageInterval#lastevent as TachographVehicleUsageIntervalInputEvent;
|
||||||
|
|
||||||
|
|
@ -180,62 +435,71 @@ select
|
||||||
c.startedAtEpochSecond as startedAtEpochSecond,
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
c.endedAtEpochSecond as endedAtEpochSecond,
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
c.durationSeconds as durationSeconds,
|
c.durationSeconds as durationSeconds,
|
||||||
sum(
|
c.unknownDurationSeconds as unknownDurationSeconds,
|
||||||
case
|
c.unknownCoveragePercent as unknownCoveragePercent,
|
||||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
|
||||||
then c.durationSeconds
|
|
||||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
|
||||||
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
|
||||||
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
|
||||||
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
|
||||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
|
||||||
end
|
|
||||||
) as unknownDurationSeconds,
|
|
||||||
(sum(
|
|
||||||
case
|
|
||||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
|
||||||
then c.durationSeconds
|
|
||||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
|
||||||
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
|
||||||
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
|
||||||
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
|
||||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
|
||||||
end
|
|
||||||
) * 100.0d) / c.durationSeconds as unknownCoveragePercent,
|
|
||||||
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
c.previousRegistrationKey as previousRegistrationKey,
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
c.nextRegistrationKey as nextRegistrationKey,
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
c.previousVehicleKey as previousVehicleKey,
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
c.nextVehicleKey as nextVehicleKey
|
c.nextVehicleKey as nextVehicleKey
|
||||||
from DrivingInterruptionVehicleChangeInterval as c unidirectional,
|
from DailyWeeklyRestCandidateCoverageInterval as c
|
||||||
VuCardAbsentInterval#keepall as u
|
where c.previousRegistrationKey is not null
|
||||||
where u.driverKey = c.driverKey
|
and c.nextRegistrationKey is not null
|
||||||
and u.startedAtEpochSecond < c.endedAtEpochSecond
|
and c.previousRegistrationKey != c.nextRegistrationKey
|
||||||
and u.endedAtEpochSecond > c.startedAtEpochSecond
|
and c.unknownDurationSeconds * 100L >= c.durationSeconds * 95L;
|
||||||
group by
|
|
||||||
c.sessionId,
|
insert into PotentialInVehicleOvernightStayInterval
|
||||||
c.driverKey,
|
select
|
||||||
c.startedAtEpochSecond,
|
c.sessionId as sessionId,
|
||||||
c.endedAtEpochSecond,
|
c.driverKey as driverKey,
|
||||||
c.durationSeconds,
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
c.previousDrivingSourceIntervalId,
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
c.nextDrivingSourceIntervalId,
|
c.durationSeconds as durationSeconds,
|
||||||
c.previousRegistrationKey,
|
c.cardPresentDurationSeconds as cardPresentDurationSeconds,
|
||||||
c.nextRegistrationKey,
|
c.cardPresentCoveragePercent as cardPresentCoveragePercent,
|
||||||
c.previousVehicleKey,
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
c.nextVehicleKey
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
having sum(
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
case
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond and u.endedAtEpochSecond >= c.endedAtEpochSecond
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
then c.durationSeconds
|
c.nextVehicleKey as nextVehicleKey
|
||||||
when u.startedAtEpochSecond <= c.startedAtEpochSecond
|
from DailyWeeklyRestCandidateCoverageInterval as c
|
||||||
then u.endedAtEpochSecond - c.startedAtEpochSecond
|
where c.previousRegistrationKey is not null
|
||||||
when u.endedAtEpochSecond >= c.endedAtEpochSecond
|
and c.nextRegistrationKey is not null
|
||||||
then c.endedAtEpochSecond - u.startedAtEpochSecond
|
and c.previousRegistrationKey = c.nextRegistrationKey
|
||||||
else u.endedAtEpochSecond - u.startedAtEpochSecond
|
and c.cardPresentDurationSeconds >= c.durationSeconds;
|
||||||
end
|
|
||||||
) * 100L >= c.durationSeconds * 95L;
|
insert into UnclassifiedDailyWeeklyRestCandidateCoverageInterval
|
||||||
|
select
|
||||||
|
c.sessionId as sessionId,
|
||||||
|
c.driverKey as driverKey,
|
||||||
|
c.startedAtEpochSecond as startedAtEpochSecond,
|
||||||
|
c.endedAtEpochSecond as endedAtEpochSecond,
|
||||||
|
c.durationSeconds as durationSeconds,
|
||||||
|
c.cardPresentDurationSeconds as cardPresentDurationSeconds,
|
||||||
|
c.cardPresentCoveragePercent as cardPresentCoveragePercent,
|
||||||
|
c.unknownDurationSeconds as unknownDurationSeconds,
|
||||||
|
c.unknownCoveragePercent as unknownCoveragePercent,
|
||||||
|
c.previousDrivingSourceIntervalId as previousDrivingSourceIntervalId,
|
||||||
|
c.nextDrivingSourceIntervalId as nextDrivingSourceIntervalId,
|
||||||
|
c.previousRegistrationKey as previousRegistrationKey,
|
||||||
|
c.nextRegistrationKey as nextRegistrationKey,
|
||||||
|
c.previousVehicleKey as previousVehicleKey,
|
||||||
|
c.nextVehicleKey as nextVehicleKey
|
||||||
|
from DailyWeeklyRestCandidateCoverageInterval as c
|
||||||
|
where not (
|
||||||
|
c.previousRegistrationKey is not null
|
||||||
|
and c.nextRegistrationKey is not null
|
||||||
|
and c.previousRegistrationKey != c.nextRegistrationKey
|
||||||
|
and c.unknownDurationSeconds * 100L >= c.durationSeconds * 95L
|
||||||
|
)
|
||||||
|
and not (
|
||||||
|
c.previousRegistrationKey is not null
|
||||||
|
and c.nextRegistrationKey is not null
|
||||||
|
and c.previousRegistrationKey = c.nextRegistrationKey
|
||||||
|
and c.cardPresentDurationSeconds >= c.durationSeconds
|
||||||
|
);
|
||||||
|
|
||||||
@name('drivingInterruptionIntervals')
|
@name('drivingInterruptionIntervals')
|
||||||
select * from DrivingInterruptionInterval;
|
select * from DrivingInterruptionInterval;
|
||||||
|
|
@ -243,6 +507,9 @@ select * from DrivingInterruptionInterval;
|
||||||
@name('dailyWeeklyRestCandidateIntervals')
|
@name('dailyWeeklyRestCandidateIntervals')
|
||||||
select * from DailyWeeklyRestCandidateInterval;
|
select * from DailyWeeklyRestCandidateInterval;
|
||||||
|
|
||||||
|
@name('dailyWeeklyRestCandidateCoverageIntervals')
|
||||||
|
select * from DailyWeeklyRestCandidateCoverageInterval;
|
||||||
|
|
||||||
@name('drivingInterruptionVehicleChangeIntervals')
|
@name('drivingInterruptionVehicleChangeIntervals')
|
||||||
select * from DrivingInterruptionVehicleChangeInterval;
|
select * from DrivingInterruptionVehicleChangeInterval;
|
||||||
|
|
||||||
|
|
@ -251,3 +518,9 @@ select * from VuCardAbsentInterval;
|
||||||
|
|
||||||
@name('potentialHomeOvernightStayIntervals')
|
@name('potentialHomeOvernightStayIntervals')
|
||||||
select * from PotentialHomeOvernightStayInterval;
|
select * from PotentialHomeOvernightStayInterval;
|
||||||
|
|
||||||
|
@name('potentialInVehicleOvernightStayIntervals')
|
||||||
|
select * from PotentialInVehicleOvernightStayInterval;
|
||||||
|
|
||||||
|
@name('unclassifiedDailyWeeklyRestCandidateCoverageIntervals')
|
||||||
|
select * from UnclassifiedDailyWeeklyRestCandidateCoverageInterval;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
package at.procon.eventhub.processing.api;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import at.procon.eventhub.dto.DriverRefDto;
|
||||||
|
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.dto.VehicleRefDto;
|
||||||
|
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.UnifiedRuntimeEventBundle;
|
||||||
|
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
|
||||||
|
import at.procon.eventhub.processing.service.UnifiedRuntimeDriverTimelineService;
|
||||||
|
import at.procon.eventhub.processing.service.UnifiedRuntimeEventAssemblyService;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedActivityInterval;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
|
class UnifiedRuntimeProcessingControllerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadsDriverEventsViaRuntimeApi() throws Exception {
|
||||||
|
UnifiedRuntimeEventAssemblyService eventAssemblyService = org.mockito.Mockito.mock(UnifiedRuntimeEventAssemblyService.class);
|
||||||
|
UnifiedRuntimeDriverTimelineService timelineService = org.mockito.Mockito.mock(UnifiedRuntimeDriverTimelineService.class);
|
||||||
|
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new UnifiedRuntimeProcessingController(eventAssemblyService, timelineService))
|
||||||
|
.setControllerAdvice(new UnifiedRuntimeProcessingExceptionHandler())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
UUID sessionId = UUID.randomUUID();
|
||||||
|
UnifiedRuntimeProcessingRequest request = UnifiedRuntimeProcessingRequest.forTachographFileSession(
|
||||||
|
sessionId,
|
||||||
|
"12:123",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
true,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
when(eventAssemblyService.assembleDriverScopedEvents(any()))
|
||||||
|
.thenReturn(new UnifiedRuntimeEventBundle(
|
||||||
|
request,
|
||||||
|
List.of(event("EV-1", "2026-05-01T08:00:00Z")),
|
||||||
|
List.of(new UnifiedDiscoveredVehicleRef("VEH-1", "VIN-1", "12", "REG-1")),
|
||||||
|
List.of(),
|
||||||
|
List.of(event("EV-1", "2026-05-01T08:00:00Z")),
|
||||||
|
List.of("note")
|
||||||
|
));
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/eventhub/runtime-processing/driver-events")
|
||||||
|
.contentType("application/json")
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"sessionId": "%s",
|
||||||
|
"sourceFamilies": ["TACHOGRAPH_FILE_SESSION"],
|
||||||
|
"driverKey": "12:123",
|
||||||
|
"occurredFrom": "2026-05-01T08:00:00Z",
|
||||||
|
"occurredTo": "2026-05-01T10:00:00Z",
|
||||||
|
"expandVehicleEvents": true,
|
||||||
|
"vehicleExpansionPaddingMinutes": 0
|
||||||
|
}
|
||||||
|
""".formatted(sessionId)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.request.sessionId").value(sessionId.toString()))
|
||||||
|
.andExpect(jsonPath("$.request.driverKey").value("12:123"))
|
||||||
|
.andExpect(jsonPath("$.discoveredVehicles[0].vin").value("VIN-1"))
|
||||||
|
.andExpect(jsonPath("$.mergedEvents[0].externalSourceEventId").value("EV-1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void loadsDriverTimelineViaRuntimeApi() throws Exception {
|
||||||
|
UnifiedRuntimeEventAssemblyService eventAssemblyService = org.mockito.Mockito.mock(UnifiedRuntimeEventAssemblyService.class);
|
||||||
|
UnifiedRuntimeDriverTimelineService timelineService = org.mockito.Mockito.mock(UnifiedRuntimeDriverTimelineService.class);
|
||||||
|
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new UnifiedRuntimeProcessingController(eventAssemblyService, timelineService))
|
||||||
|
.setControllerAdvice(new UnifiedRuntimeProcessingExceptionHandler())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
when(timelineService.loadDriverTimeline(any()))
|
||||||
|
.thenReturn(new ResolvedDriverTimeline(
|
||||||
|
"UNIFIED_EVENT_STREAM",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
List.of(ResolvedVehicleUsageInterval.resolved(
|
||||||
|
null,
|
||||||
|
"DRIVER:42",
|
||||||
|
"CVU-1",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
100L,
|
||||||
|
200L,
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"DRIVER_CARD",
|
||||||
|
List.of("CVU-1")
|
||||||
|
)),
|
||||||
|
List.of(new ResolvedActivityInterval(
|
||||||
|
"ACT-1",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:30:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T09:00:00Z"),
|
||||||
|
1800L,
|
||||||
|
"DRIVE",
|
||||||
|
"DRIVER",
|
||||||
|
"INSERTED",
|
||||||
|
"SINGLE",
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"DRIVER_CARD",
|
||||||
|
List.of("ACT-1"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"RAW_INTERVAL"
|
||||||
|
)),
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
));
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/eventhub/runtime-processing/driver-timeline")
|
||||||
|
.contentType("application/json")
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"tenantKey": "default",
|
||||||
|
"sourceFamilies": ["TACHOGRAPH_DB", "YELLOWFOX_DB"],
|
||||||
|
"eventBackend": "SOURCE_DB",
|
||||||
|
"driverSourceEntityId": "DRIVER:42",
|
||||||
|
"occurredFrom": "2026-05-01T08:00:00Z",
|
||||||
|
"occurredTo": "2026-05-01T10:00:00Z",
|
||||||
|
"expandVehicleEvents": false,
|
||||||
|
"vehicleExpansionPaddingMinutes": 15
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.sourceKind").value("UNIFIED_EVENT_STREAM"))
|
||||||
|
.andExpect(jsonPath("$.activityIntervals[0].intervalId").value("ACT-1"))
|
||||||
|
.andExpect(jsonPath("$.vehicleUsageIntervals[0].intervalId").value("CVU-1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsBadRequestForInvalidRuntimeRequest() throws Exception {
|
||||||
|
UnifiedRuntimeEventAssemblyService eventAssemblyService = org.mockito.Mockito.mock(UnifiedRuntimeEventAssemblyService.class);
|
||||||
|
UnifiedRuntimeDriverTimelineService timelineService = org.mockito.Mockito.mock(UnifiedRuntimeDriverTimelineService.class);
|
||||||
|
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new UnifiedRuntimeProcessingController(eventAssemblyService, timelineService))
|
||||||
|
.setControllerAdvice(new UnifiedRuntimeProcessingExceptionHandler())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/eventhub/runtime-processing/driver-events")
|
||||||
|
.contentType("application/json")
|
||||||
|
.content("""
|
||||||
|
{
|
||||||
|
"sourceFamilies": ["TACHOGRAPH_DB"],
|
||||||
|
"occurredFrom": "2026-05-01T08:00:00Z",
|
||||||
|
"occurredTo": "2026-05-01T10:00:00Z"
|
||||||
|
}
|
||||||
|
"""))
|
||||||
|
.andExpect(status().isBadRequest())
|
||||||
|
.andExpect(jsonPath("$.message").exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHubEventDto event(String externalId, String occurredAt) {
|
||||||
|
return new EventHubEventDto(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
externalId,
|
||||||
|
new DriverRefDto("12:123", null),
|
||||||
|
new VehicleRefDto("VEH-1", "VIN-1", "VR-1", new VehicleRegistrationRefDto("12", 12, "REG-1")),
|
||||||
|
OffsetDateTime.parse(occurredAt),
|
||||||
|
null,
|
||||||
|
OffsetDateTime.parse(occurredAt),
|
||||||
|
EventDomain.DRIVER_ACTIVITY,
|
||||||
|
EventType.DRIVE,
|
||||||
|
EventLifecycle.START,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDri
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperVehicleUsageIntervalEvent;
|
||||||
|
|
@ -85,6 +86,9 @@ class TachographFileSessionControllerTest {
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
List.of(new TachographEsperActivityIntervalEvent(
|
List.of(new TachographEsperActivityIntervalEvent(
|
||||||
|
|
@ -164,6 +168,24 @@ class TachographFileSessionControllerTest {
|
||||||
"VIN-1",
|
"VIN-1",
|
||||||
"VIN-2"
|
"VIN-2"
|
||||||
)),
|
)),
|
||||||
|
List.of(new TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent(
|
||||||
|
sessionId,
|
||||||
|
"12:123",
|
||||||
|
OffsetDateTime.parse("2026-05-12T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-12T22:00:00Z"),
|
||||||
|
43_200L,
|
||||||
|
0L,
|
||||||
|
0.0d,
|
||||||
|
43_200L,
|
||||||
|
100.0d,
|
||||||
|
"ACT-2",
|
||||||
|
"ACT-3",
|
||||||
|
"12:REG-1",
|
||||||
|
"12:REG-2",
|
||||||
|
"VIN-1",
|
||||||
|
"VIN-2"
|
||||||
|
)),
|
||||||
|
List.of(),
|
||||||
List.of(new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
List.of(new TachographEsperPotentialHomeOvernightStayIntervalEvent(
|
||||||
sessionId,
|
sessionId,
|
||||||
"12:123",
|
"12:123",
|
||||||
|
|
@ -179,6 +201,7 @@ class TachographFileSessionControllerTest {
|
||||||
"VIN-1",
|
"VIN-1",
|
||||||
"VIN-2"
|
"VIN-2"
|
||||||
)),
|
)),
|
||||||
|
List.of(),
|
||||||
List.of(new TachographEsperVehicleUsageIntervalEvent(
|
List.of(new TachographEsperVehicleUsageIntervalEvent(
|
||||||
sessionId,
|
sessionId,
|
||||||
"12:123",
|
"12:123",
|
||||||
|
|
@ -273,7 +296,10 @@ class TachographFileSessionControllerTest {
|
||||||
.andExpect(jsonPath("$.drivingInterruptionIntervalCount").value(1))
|
.andExpect(jsonPath("$.drivingInterruptionIntervalCount").value(1))
|
||||||
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervalCount").value(1))
|
.andExpect(jsonPath("$.drivingInterruptionVehicleChangeIntervalCount").value(1))
|
||||||
.andExpect(jsonPath("$.dailyWeeklyRestCandidateIntervalCount").value(1))
|
.andExpect(jsonPath("$.dailyWeeklyRestCandidateIntervalCount").value(1))
|
||||||
|
.andExpect(jsonPath("$.dailyWeeklyRestCandidateCoverageIntervalCount").value(1))
|
||||||
|
.andExpect(jsonPath("$.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount").value(0))
|
||||||
.andExpect(jsonPath("$.potentialHomeOvernightStayIntervalCount").value(1))
|
.andExpect(jsonPath("$.potentialHomeOvernightStayIntervalCount").value(1))
|
||||||
|
.andExpect(jsonPath("$.potentialInVehicleOvernightStayIntervalCount").value(0))
|
||||||
.andExpect(jsonPath("$.vuCardAbsentIntervalCount").value(1))
|
.andExpect(jsonPath("$.vuCardAbsentIntervalCount").value(1))
|
||||||
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].previousRegistrationKey").value("12:REG-1"))
|
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].previousRegistrationKey").value("12:REG-1"))
|
||||||
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].nextRegistrationKey").value("12:REG-2"))
|
.andExpect(jsonPath("$.drivingInterruptionIntervals[0].nextRegistrationKey").value("12:REG-2"))
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInter
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
|
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
|
||||||
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingDerivedProjectionBundle;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDrivingInterruptionIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperPotentialHomeOvernightStayIntervalEvent;
|
||||||
|
|
@ -122,8 +123,99 @@ class DriverTimelineReusableProjectionBuilderTest {
|
||||||
|
|
||||||
assertThat(reusableBundle.drivingInterruptionIntervals()).containsExactlyElementsOf(legacyDrivingInterruptions);
|
assertThat(reusableBundle.drivingInterruptionIntervals()).containsExactlyElementsOf(legacyDrivingInterruptions);
|
||||||
assertThat(reusableBundle.dailyWeeklyRestCandidateIntervals()).containsExactlyElementsOf(legacyDailyWeeklyRestCandidates);
|
assertThat(reusableBundle.dailyWeeklyRestCandidateIntervals()).containsExactlyElementsOf(legacyDailyWeeklyRestCandidates);
|
||||||
|
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals()).hasSize(1);
|
||||||
|
TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent coverageInterval =
|
||||||
|
reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0);
|
||||||
|
assertThat(coverageInterval.unknownCoveragePercent()).isCloseTo(99.998d, org.assertj.core.data.Offset.offset(0.001d));
|
||||||
|
assertThat(coverageInterval.cardPresentCoveragePercent()).isEqualTo(0.0d);
|
||||||
assertThat(reusableBundle.drivingInterruptionVehicleChangeIntervals()).containsExactlyElementsOf(legacyDrivingInterruptionVehicleChanges);
|
assertThat(reusableBundle.drivingInterruptionVehicleChangeIntervals()).containsExactlyElementsOf(legacyDrivingInterruptionVehicleChanges);
|
||||||
assertThat(reusableBundle.vuCardAbsentIntervals()).containsExactlyElementsOf(legacyVuCardAbsentIntervals);
|
assertThat(reusableBundle.vuCardAbsentIntervals()).containsExactlyElementsOf(legacyVuCardAbsentIntervals);
|
||||||
assertThat(reusableBundle.potentialHomeOvernightStayIntervals()).containsExactlyElementsOf(legacyPotentialHomeOvernightStays);
|
assertThat(reusableBundle.potentialHomeOvernightStayIntervals()).containsExactlyElementsOf(legacyPotentialHomeOvernightStays);
|
||||||
|
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void buildsPotentialInVehicleOvernightStayIntervalsFromVehicleUnchangedCoveredRest() {
|
||||||
|
DriverExtractionSession driver = new DriverExtractionSession(
|
||||||
|
"12:123",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(
|
||||||
|
new ExtractedCardVehicleUsageInterval(
|
||||||
|
"CVU-1",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-02T01:00:00Z"),
|
||||||
|
100L,
|
||||||
|
260L,
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"vu-1"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
List.of(
|
||||||
|
new ExtractedCardActivityInterval(
|
||||||
|
"ACT-1",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
"DRIVE",
|
||||||
|
"DRIVER",
|
||||||
|
"INSERTED",
|
||||||
|
"SINGLE",
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"a"
|
||||||
|
),
|
||||||
|
new ExtractedCardActivityInterval(
|
||||||
|
"ACT-2",
|
||||||
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-02T00:30:00Z"),
|
||||||
|
"DRIVE",
|
||||||
|
"DRIVER",
|
||||||
|
"INSERTED",
|
||||||
|
"SINGLE",
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"b"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
TachographFileSession session = new TachographFileSession(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 2, "42", "b", true, null),
|
||||||
|
Map.of(driver.driverKey(), driver),
|
||||||
|
new ExtractionStats(1, 2, 1, 1, 1, 0),
|
||||||
|
List.of(),
|
||||||
|
Instant.now(),
|
||||||
|
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||||
|
);
|
||||||
|
|
||||||
|
ResolvedDriverTimeline timeline = legacyBuilder.build(session, driver);
|
||||||
|
TachographEsperDrivingDerivedProjectionBundle reusableBundle =
|
||||||
|
reusableBuilder.buildEsperDrivingDerivedProjectionBundle(
|
||||||
|
session.sessionId(),
|
||||||
|
driver.driverKey(),
|
||||||
|
timeline,
|
||||||
|
3,
|
||||||
|
720
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(reusableBundle.drivingInterruptionVehicleChangeIntervals()).isEmpty();
|
||||||
|
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals()).hasSize(1);
|
||||||
|
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).cardPresentCoveragePercent())
|
||||||
|
.isEqualTo(100.0d);
|
||||||
|
assertThat(reusableBundle.dailyWeeklyRestCandidateCoverageIntervals().get(0).unknownCoveragePercent())
|
||||||
|
.isEqualTo(0.0d);
|
||||||
|
assertThat(reusableBundle.potentialHomeOvernightStayIntervals()).isEmpty();
|
||||||
|
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals()).hasSize(1);
|
||||||
|
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).startedAt())
|
||||||
|
.isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||||
|
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).endedAt())
|
||||||
|
.isEqualTo(OffsetDateTime.parse("2026-05-02T00:00:00Z"));
|
||||||
|
assertThat(reusableBundle.potentialInVehicleOvernightStayIntervals().get(0).cardPresentCoveragePercent())
|
||||||
|
.isEqualTo(100.0d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,10 @@ class TachographFileSessionProcessingServiceTest {
|
||||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(0);
|
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(0);
|
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.vehicleUsageIntervalCount()).isEqualTo(2);
|
assertThat(result.vehicleUsageIntervalCount()).isEqualTo(2);
|
||||||
assertThat(result.vuCardAbsentIntervalCount()).isEqualTo(1);
|
assertThat(result.vuCardAbsentIntervalCount()).isEqualTo(1);
|
||||||
assertThat(result.vuCardAbsentIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:01Z"));
|
assertThat(result.vuCardAbsentIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:01Z"));
|
||||||
|
|
@ -194,7 +197,10 @@ class TachographFileSessionProcessingServiceTest {
|
||||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(1);
|
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(1);
|
||||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(0);
|
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.drivingInterruptionIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
|
assertThat(result.drivingInterruptionIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T09:00:00Z"));
|
||||||
assertThat(result.drivingInterruptionIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
assertThat(result.drivingInterruptionIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||||
assertThat(result.drivingInterruptionIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
assertThat(result.drivingInterruptionIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||||
|
|
@ -291,6 +297,9 @@ class TachographFileSessionProcessingServiceTest {
|
||||||
assertThat(result.activityIntervalCount()).isEqualTo(3);
|
assertThat(result.activityIntervalCount()).isEqualTo(3);
|
||||||
assertThat(result.drivingIntervalCount()).isEqualTo(2);
|
assertThat(result.drivingIntervalCount()).isEqualTo(2);
|
||||||
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(1);
|
assertThat(result.drivingInterruptionIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.vehicleUsageIntervalCount()).isEqualTo(2);
|
assertThat(result.vehicleUsageIntervalCount()).isEqualTo(2);
|
||||||
assertThat(result.vuCardAbsentIntervalCount()).isEqualTo(1);
|
assertThat(result.vuCardAbsentIntervalCount()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
@ -373,19 +382,200 @@ class TachographFileSessionProcessingServiceTest {
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(1);
|
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(1);
|
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(1);
|
||||||
assertThat(result.dailyWeeklyRestCandidateIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
assertThat(result.dailyWeeklyRestCandidateIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||||
assertThat(result.dailyWeeklyRestCandidateIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T23:00:00Z"));
|
assertThat(result.dailyWeeklyRestCandidateIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T23:00:00Z"));
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).unknownDurationSeconds()).isEqualTo(43_200L);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).unknownCoveragePercent()).isEqualTo(100.0d);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).cardPresentDurationSeconds()).isEqualTo(0L);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).cardPresentCoveragePercent()).isEqualTo(0.0d);
|
||||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).previousRegistrationKey()).isEqualTo("12:REG-1");
|
||||||
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
assertThat(result.drivingInterruptionVehicleChangeIntervals().get(0).nextRegistrationKey()).isEqualTo("12:REG-2");
|
||||||
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(1);
|
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
assertThat(result.potentialHomeOvernightStayIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
|
||||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T23:00:00Z"));
|
assertThat(result.potentialHomeOvernightStayIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T23:00:00Z"));
|
||||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).unknownDurationSeconds()).isEqualTo(43_200L);
|
assertThat(result.potentialHomeOvernightStayIntervals().get(0).unknownDurationSeconds()).isEqualTo(43_200L);
|
||||||
assertThat(result.potentialHomeOvernightStayIntervals().get(0).unknownCoveragePercent()).isEqualTo(100.0d);
|
assertThat(result.potentialHomeOvernightStayIntervals().get(0).unknownCoveragePercent()).isEqualTo(100.0d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsPotentialInVehicleOvernightStayIntervalsWhenVehicleUnchangedAndCardRemainsInserted() {
|
||||||
|
EventHubProperties properties = new EventHubProperties();
|
||||||
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
DriverTimelineBuilder driverTimelineBuilder = new DriverTimelineBuilder();
|
||||||
|
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||||
|
repository,
|
||||||
|
driverTimelineBuilder,
|
||||||
|
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||||
|
new EventBackedDriverTimelineBuilder(
|
||||||
|
new IntervalBackedDriverTimelineEventBuilder(
|
||||||
|
driverTimelineBuilder,
|
||||||
|
new DriverKeyFactory(),
|
||||||
|
new VehicleKeyFactory(),
|
||||||
|
new EventDetailsFactory(new ObjectMapper())
|
||||||
|
)
|
||||||
|
),
|
||||||
|
properties
|
||||||
|
);
|
||||||
|
|
||||||
|
DriverExtractionSession driver = new DriverExtractionSession(
|
||||||
|
"12:123",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(
|
||||||
|
new ExtractedCardVehicleUsageInterval(
|
||||||
|
"CVU-1",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-02T01:00:00Z"),
|
||||||
|
100L,
|
||||||
|
260L,
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"vu-1"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
List.of(
|
||||||
|
new ExtractedCardActivityInterval("ACT-1", OffsetDateTime.parse("2026-05-01T08:00:00Z"), OffsetDateTime.parse("2026-05-01T10:00:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "a"),
|
||||||
|
new ExtractedCardActivityInterval("ACT-2", OffsetDateTime.parse("2026-05-02T00:00:00Z"), OffsetDateTime.parse("2026-05-02T00:30:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "b")
|
||||||
|
),
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
TachographFileSession session = new TachographFileSession(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 2, "42", "b", true, null),
|
||||||
|
Map.of(driver.driverKey(), driver),
|
||||||
|
new ExtractionStats(1, 2, 1, 1, 1, 0),
|
||||||
|
List.of(),
|
||||||
|
Instant.now(),
|
||||||
|
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||||
|
);
|
||||||
|
repository.save(session);
|
||||||
|
|
||||||
|
TachographEsperDriverProcessingResultDto result = service.getEsperDriverProcessingResults(
|
||||||
|
session.sessionId(),
|
||||||
|
driver.driverKey(),
|
||||||
|
new TachographEsperEventsProcessingRequest(
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-02T00:00:00Z"),
|
||||||
|
3,
|
||||||
|
720
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.drivingInterruptionVehicleChangeIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).cardPresentDurationSeconds()).isEqualTo(50_400L);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).cardPresentCoveragePercent()).isEqualTo(100.0d);
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervals().get(0).unknownDurationSeconds()).isEqualTo(0L);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervals().get(0).startedAt()).isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervals().get(0).endedAt()).isEqualTo(OffsetDateTime.parse("2026-05-02T00:00:00Z"));
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervals().get(0).cardPresentDurationSeconds()).isEqualTo(50_400L);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervals().get(0).cardPresentCoveragePercent()).isEqualTo(100.0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsUnclassifiedDailyWeeklyRestCandidateCoverageIntervalsWhenRestCandidateFitsNeitherCategory() {
|
||||||
|
EventHubProperties properties = new EventHubProperties();
|
||||||
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
DriverTimelineBuilder driverTimelineBuilder = new DriverTimelineBuilder();
|
||||||
|
TachographFileSessionProcessingService service = new TachographFileSessionProcessingService(
|
||||||
|
repository,
|
||||||
|
driverTimelineBuilder,
|
||||||
|
new DriverTimelineReusableProjectionBuilder(driverTimelineBuilder),
|
||||||
|
new EventBackedDriverTimelineBuilder(
|
||||||
|
new IntervalBackedDriverTimelineEventBuilder(
|
||||||
|
driverTimelineBuilder,
|
||||||
|
new DriverKeyFactory(),
|
||||||
|
new VehicleKeyFactory(),
|
||||||
|
new EventDetailsFactory(new ObjectMapper())
|
||||||
|
)
|
||||||
|
),
|
||||||
|
properties
|
||||||
|
);
|
||||||
|
|
||||||
|
DriverExtractionSession driver = new DriverExtractionSession(
|
||||||
|
"12:123",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(),
|
||||||
|
List.of(),
|
||||||
|
List.of(
|
||||||
|
new ExtractedCardVehicleUsageInterval(
|
||||||
|
"CVU-1",
|
||||||
|
OffsetDateTime.parse("2026-05-01T08:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-01T14:00:00Z"),
|
||||||
|
100L,
|
||||||
|
200L,
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"vu-1"
|
||||||
|
),
|
||||||
|
new ExtractedCardVehicleUsageInterval(
|
||||||
|
"CVU-2",
|
||||||
|
OffsetDateTime.parse("2026-05-01T18:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-02T02:00:00Z"),
|
||||||
|
201L,
|
||||||
|
260L,
|
||||||
|
"12:REG-1",
|
||||||
|
"VIN-1",
|
||||||
|
"vu-2"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
List.of(
|
||||||
|
new ExtractedCardActivityInterval("ACT-1", OffsetDateTime.parse("2026-05-01T08:00:00Z"), OffsetDateTime.parse("2026-05-01T10:00:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "a"),
|
||||||
|
new ExtractedCardActivityInterval("ACT-2", OffsetDateTime.parse("2026-05-02T00:30:00Z"), OffsetDateTime.parse("2026-05-02T01:00:00Z"), "DRIVE", "DRIVER", "INSERTED", "SINGLE", "12:REG-1", "VIN-1", "b")
|
||||||
|
),
|
||||||
|
List.of(),
|
||||||
|
List.of()
|
||||||
|
);
|
||||||
|
TachographFileSession session = new TachographFileSession(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 2, "42", "b", true, null),
|
||||||
|
Map.of(driver.driverKey(), driver),
|
||||||
|
new ExtractionStats(1, 2, 2, 1, 1, 0),
|
||||||
|
List.of(),
|
||||||
|
Instant.now(),
|
||||||
|
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||||
|
);
|
||||||
|
repository.save(session);
|
||||||
|
|
||||||
|
TachographEsperDriverProcessingResultDto result = service.getEsperDriverProcessingResults(
|
||||||
|
session.sessionId(),
|
||||||
|
driver.driverKey(),
|
||||||
|
new TachographEsperEventsProcessingRequest(
|
||||||
|
OffsetDateTime.parse("2026-05-01T10:00:00Z"),
|
||||||
|
OffsetDateTime.parse("2026-05-02T00:30:00Z"),
|
||||||
|
3,
|
||||||
|
720
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(result.dailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.potentialHomeOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.potentialInVehicleOvernightStayIntervalCount()).isEqualTo(0);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervalCount()).isEqualTo(1);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervals().get(0).startedAt())
|
||||||
|
.isEqualTo(OffsetDateTime.parse("2026-05-01T10:00:00Z"));
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervals().get(0).endedAt())
|
||||||
|
.isEqualTo(OffsetDateTime.parse("2026-05-02T00:30:00Z"));
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervals().get(0).cardPresentCoveragePercent())
|
||||||
|
.isLessThan(95.0d);
|
||||||
|
assertThat(result.unclassifiedDailyWeeklyRestCandidateCoverageIntervals().get(0).unknownCoveragePercent())
|
||||||
|
.isLessThan(95.0d);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void evaluatesOperatingPeriodsFromSessionTimeline() {
|
void evaluatesOperatingPeriodsFromSessionTimeline() {
|
||||||
EventHubProperties properties = new EventHubProperties();
|
EventHubProperties properties = new EventHubProperties();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue