Add runtime vehicle evidence debug output
This commit is contained in:
parent
b04b333db7
commit
e68047feab
|
|
@ -0,0 +1,17 @@
|
|||
# EventHub fix-list patch
|
||||
|
||||
This patch implements the requested remaining items from the fix list, excluding build verification and SQL Server 2008 SQL rewriting.
|
||||
|
||||
## Apply notes
|
||||
|
||||
1. Copy the files from this archive into the project root.
|
||||
2. Delete the old migration files listed in `DELETE_FILES.txt`.
|
||||
3. Run the test suite locally with Maven/Java 21.
|
||||
|
||||
## Main changes
|
||||
|
||||
- Renumbered Flyway migrations to remove duplicate `V9` and `V10` versions.
|
||||
- Removed the duplicated Timescale/Event source record migration.
|
||||
- Switched local Docker Compose DB from plain PostgreSQL to a TimescaleDB/PostGIS-capable image.
|
||||
- Added normalized raw tachograph payload metadata for DB-extracted EventHub events.
|
||||
- Added tests for Flyway version uniqueness and tachograph DB mapper → timeline reconstruction metadata.
|
||||
|
|
@ -29,7 +29,8 @@ Example response:
|
|||
"significantDrivingMinutes",
|
||||
"minimumRestPeriodMinutes",
|
||||
"attachVehicleOnlyEvents",
|
||||
"vehicleEvidencePaddingMinutes"
|
||||
"vehicleEvidencePaddingMinutes",
|
||||
"includePartitionDebug"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -63,13 +64,15 @@ POST /api/eventhub/runtime-processing/event-processing
|
|||
"strategy": "DRIVER",
|
||||
"includeAllPartitions": true,
|
||||
"attachVehicleEvidence": true,
|
||||
"vehicleEvidencePaddingMinutes": 15
|
||||
"vehicleEvidencePaddingMinutes": 15,
|
||||
"includeDebug": true
|
||||
},
|
||||
"parameters": {
|
||||
"significantDrivingMinutes": 3,
|
||||
"minimumRestPeriodMinutes": 720,
|
||||
"attachVehicleOnlyEvents": true,
|
||||
"vehicleEvidencePaddingMinutes": 15
|
||||
"vehicleEvidencePaddingMinutes": 15,
|
||||
"includePartitionDebug": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -182,6 +185,51 @@ CUSTOM_PROFILE
|
|||
|
||||
The first tachograph profile currently supports `DRIVER` partitioning. The service partitions mixed event scopes in Java before invoking Esper so that existing single-driver EPL windows cannot mix driver states.
|
||||
|
||||
## Partition debug / audit output
|
||||
|
||||
For mixed-source scopes, request debug output when validating why events were or were not attached to a driver partition:
|
||||
|
||||
```json
|
||||
{
|
||||
"partitioning": {
|
||||
"strategy": "DRIVER",
|
||||
"includeAllPartitions": true,
|
||||
"attachVehicleEvidence": true,
|
||||
"vehicleEvidencePaddingMinutes": 15,
|
||||
"includeDebug": true
|
||||
},
|
||||
"parameters": {
|
||||
"includePartitionDebug": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When enabled, each generic `partitionResults[*].metadata.partitionDebug` contains:
|
||||
|
||||
```text
|
||||
directDriverEventCount
|
||||
vehicleUsageIntervalCount
|
||||
candidateVehicleEvidenceEventCount
|
||||
attachedVehicleEvidenceEventCount
|
||||
ignoredVehicleEvidenceEventCount
|
||||
mergedEventCount
|
||||
vehicleUsageIntervals
|
||||
vehicleEvidenceDecisions
|
||||
notes
|
||||
warnings
|
||||
```
|
||||
|
||||
`vehicleEvidenceDecisions` explains every relevant decision, for example:
|
||||
|
||||
```text
|
||||
DIRECT_DRIVER_EVENT
|
||||
ATTACHED_VEHICLE_EVIDENCE
|
||||
IGNORED_NO_OVERLAPPING_VEHICLE_USAGE
|
||||
IGNORED_ATTACHMENT_DISABLED
|
||||
```
|
||||
|
||||
The compatibility response type also has `partitionDebugByDriver`; use the generic endpoint when you need to enable debug output explicitly. Keep debug disabled for high-volume production requests unless you need attribution diagnostics, because the decision list can be large.
|
||||
|
||||
## Compatibility endpoint
|
||||
|
||||
The old tachograph endpoint remains available:
|
||||
|
|
@ -233,11 +281,13 @@ This prevents unrelated vehicle events from being copied into a driver result si
|
|||
{
|
||||
"partitioning": {
|
||||
"attachVehicleEvidence": true,
|
||||
"vehicleEvidencePaddingMinutes": 15
|
||||
"vehicleEvidencePaddingMinutes": 15,
|
||||
"includeDebug": true
|
||||
},
|
||||
"parameters": {
|
||||
"attachVehicleOnlyEvents": true,
|
||||
"vehicleEvidencePaddingMinutes": 15
|
||||
"vehicleEvidencePaddingMinutes": 15,
|
||||
"includePartitionDebug": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -47,3 +47,8 @@ to the generic profile behavior:
|
|||
```
|
||||
|
||||
Attachment is temporal: a vehicle-only event must match a reconstructed driver vehicle-usage interval and occur inside the interval plus configured padding.
|
||||
|
||||
|
||||
## Debugging vehicle evidence attachment
|
||||
|
||||
Prefer the generic `/api/eventhub/runtime-processing/event-processing` endpoint with `partitioning.includeDebug=true` or `parameters.includePartitionDebug=true`. The compatibility response type has `partitionDebugByDriver`, but the generic endpoint is the preferred way to enable debug output explicitly. The generic response exposes debug data under `partitionResults[*].metadata.partitionDebug`.
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"profileKey\": \"tachograph-driver-esper-v1\",\n \"scope\": {\n \"sessionId\": \"{{sessionId}}\",\n \"sourceFamilies\": [\n \"TACHOGRAPH_FILE_SESSION\"\n ],\n \"driverKey\": \"{{driverKey}}\",\n \"occurredFrom\": \"2026-05-01T00:00:00Z\",\n \"occurredTo\": \"2026-05-31T23:59:59Z\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 15\n },\n \"partitioning\": {\n \"strategy\": \"DRIVER\",\n \"partitionKeys\": [\n \"{{driverKey}}\"\n ],\n \"attachVehicleEvidence\": true,\n \"vehicleEvidencePaddingMinutes\": 15\n },\n \"parameters\": {\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720,\n \"attachVehicleOnlyEvents\": true,\n \"vehicleEvidencePaddingMinutes\": 15\n }\n}"
|
||||
"raw": "{\n \"profileKey\": \"tachograph-driver-esper-v1\",\n \"scope\": {\n \"sessionId\": \"{{sessionId}}\",\n \"sourceFamilies\": [\n \"TACHOGRAPH_FILE_SESSION\"\n ],\n \"driverKey\": \"{{driverKey}}\",\n \"occurredFrom\": \"2026-05-01T00:00:00Z\",\n \"occurredTo\": \"2026-05-31T23:59:59Z\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 15\n },\n \"partitioning\": {\n \"strategy\": \"DRIVER\",\n \"partitionKeys\": [\n \"{{driverKey}}\"\n ],\n \"attachVehicleEvidence\": true,\n \"vehicleEvidencePaddingMinutes\": 15,\n \"includeDebug\": true\n },\n \"parameters\": {\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720,\n \"attachVehicleOnlyEvents\": true,\n \"vehicleEvidencePaddingMinutes\": 15,\n \"includePartitionDebug\": true\n }\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/event-processing",
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"profileKey\": \"tachograph-driver-esper-v1\",\n \"scope\": {\n \"sessionIds\": [\n \"{{sessionId}}\",\n \"{{sessionId2}}\"\n ],\n \"sourceFamilies\": [\n \"TACHOGRAPH_FILE_SESSION\"\n ],\n \"occurredFrom\": \"2026-05-01T00:00:00Z\",\n \"occurredTo\": \"2026-05-31T23:59:59Z\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 15\n },\n \"partitioning\": {\n \"strategy\": \"DRIVER\",\n \"includeAllPartitions\": true,\n \"attachVehicleEvidence\": true,\n \"vehicleEvidencePaddingMinutes\": 15\n },\n \"parameters\": {\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720,\n \"attachVehicleOnlyEvents\": true,\n \"vehicleEvidencePaddingMinutes\": 15\n }\n}"
|
||||
"raw": "{\n \"profileKey\": \"tachograph-driver-esper-v1\",\n \"scope\": {\n \"sessionIds\": [\n \"{{sessionId}}\",\n \"{{sessionId2}}\"\n ],\n \"sourceFamilies\": [\n \"TACHOGRAPH_FILE_SESSION\"\n ],\n \"occurredFrom\": \"2026-05-01T00:00:00Z\",\n \"occurredTo\": \"2026-05-31T23:59:59Z\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 15\n },\n \"partitioning\": {\n \"strategy\": \"DRIVER\",\n \"includeAllPartitions\": true,\n \"attachVehicleEvidence\": true,\n \"vehicleEvidencePaddingMinutes\": 15,\n \"includeDebug\": true\n },\n \"parameters\": {\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720,\n \"attachVehicleOnlyEvents\": true,\n \"vehicleEvidencePaddingMinutes\": 15,\n \"includePartitionDebug\": true\n }\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/event-processing",
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"profileKey\": \"tachograph-driver-esper-v1\",\n \"scope\": {\n \"compositeSessionId\": \"{{compositeSessionId}}\",\n \"sourceFamilies\": [\n \"TACHOGRAPH_FILE_SESSION\"\n ],\n \"occurredFrom\": \"2026-05-01T00:00:00Z\",\n \"occurredTo\": \"2026-05-31T23:59:59Z\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 15\n },\n \"partitioning\": {\n \"strategy\": \"DRIVER\",\n \"includeAllPartitions\": true,\n \"attachVehicleEvidence\": true,\n \"vehicleEvidencePaddingMinutes\": 15\n },\n \"parameters\": {\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720,\n \"attachVehicleOnlyEvents\": true,\n \"vehicleEvidencePaddingMinutes\": 15\n }\n}"
|
||||
"raw": "{\n \"profileKey\": \"tachograph-driver-esper-v1\",\n \"scope\": {\n \"compositeSessionId\": \"{{compositeSessionId}}\",\n \"sourceFamilies\": [\n \"TACHOGRAPH_FILE_SESSION\"\n ],\n \"occurredFrom\": \"2026-05-01T00:00:00Z\",\n \"occurredTo\": \"2026-05-31T23:59:59Z\",\n \"expandVehicleEvents\": true,\n \"vehicleExpansionPaddingMinutes\": 15\n },\n \"partitioning\": {\n \"strategy\": \"DRIVER\",\n \"includeAllPartitions\": true,\n \"attachVehicleEvidence\": true,\n \"vehicleEvidencePaddingMinutes\": 15,\n \"includeDebug\": true\n },\n \"parameters\": {\n \"significantDrivingMinutes\": 3,\n \"minimumRestPeriodMinutes\": 720,\n \"attachVehicleOnlyEvents\": true,\n \"vehicleEvidencePaddingMinutes\": 15,\n \"includePartitionDebug\": true\n }\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/eventhub/runtime-processing/event-processing",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package at.procon.eventhub.processing.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record RuntimeDriverPartitionDebugDto(
|
||||
String driverKey,
|
||||
int directDriverEventCount,
|
||||
int vehicleUsageIntervalCount,
|
||||
int candidateVehicleEvidenceEventCount,
|
||||
int attachedVehicleEvidenceEventCount,
|
||||
int ignoredVehicleEvidenceEventCount,
|
||||
int mergedEventCount,
|
||||
List<RuntimeVehicleUsageIntervalDebugDto> vehicleUsageIntervals,
|
||||
List<RuntimeVehicleEvidenceAttachmentDecisionDto> vehicleEvidenceDecisions,
|
||||
List<String> notes,
|
||||
List<String> warnings
|
||||
) {
|
||||
public RuntimeDriverPartitionDebugDto {
|
||||
vehicleUsageIntervals = vehicleUsageIntervals == null ? List.of() : List.copyOf(vehicleUsageIntervals);
|
||||
vehicleEvidenceDecisions = vehicleEvidenceDecisions == null ? List.of() : List.copyOf(vehicleEvidenceDecisions);
|
||||
notes = notes == null ? List.of() : List.copyOf(notes);
|
||||
warnings = warnings == null ? List.of() : List.copyOf(warnings);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package at.procon.eventhub.processing.dto;
|
||||
|
||||
import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeType;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public record RuntimeVehicleEvidenceAttachmentDecisionDto(
|
||||
String decision,
|
||||
String reason,
|
||||
String eventKey,
|
||||
String externalSourceEventId,
|
||||
OffsetDateTime occurredAt,
|
||||
String eventDomain,
|
||||
String eventType,
|
||||
String lifecycle,
|
||||
RuntimeEventScopeType scopeType,
|
||||
Set<String> vehicleKeys,
|
||||
List<String> matchingVehicleUsageIntervalIds
|
||||
) {
|
||||
public RuntimeVehicleEvidenceAttachmentDecisionDto {
|
||||
vehicleKeys = vehicleKeys == null ? Set.of() : Set.copyOf(vehicleKeys);
|
||||
matchingVehicleUsageIntervalIds = matchingVehicleUsageIntervalIds == null ? List.of() : List.copyOf(matchingVehicleUsageIntervalIds);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package at.procon.eventhub.processing.dto;
|
||||
|
||||
import at.procon.eventhub.tachographfilesession.model.ResolvedVehicleUsageInterval;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public record RuntimeVehicleUsageIntervalDebugDto(
|
||||
String intervalId,
|
||||
OffsetDateTime from,
|
||||
OffsetDateTime to,
|
||||
String registrationKey,
|
||||
String vehicleKey,
|
||||
String sourceKind
|
||||
) {
|
||||
public static RuntimeVehicleUsageIntervalDebugDto from(ResolvedVehicleUsageInterval interval) {
|
||||
if (interval == null) {
|
||||
return null;
|
||||
}
|
||||
return new RuntimeVehicleUsageIntervalDebugDto(
|
||||
interval.intervalId(),
|
||||
interval.from(),
|
||||
interval.to(),
|
||||
interval.registrationKey(),
|
||||
interval.vehicleKey(),
|
||||
interval.sourceKind()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,10 +13,48 @@ public record UnifiedRuntimeDerivedProjectionResultDto(
|
|||
int mergedEventCount,
|
||||
List<UnifiedDiscoveredVehicleRef> discoveredVehicles,
|
||||
TachographEsperDriverProcessingResultDto projection,
|
||||
List<String> notes
|
||||
List<String> notes,
|
||||
RuntimeDriverPartitionDebugDto partitionDebug
|
||||
) {
|
||||
public UnifiedRuntimeDerivedProjectionResultDto {
|
||||
discoveredVehicles = discoveredVehicles == null ? List.of() : List.copyOf(discoveredVehicles);
|
||||
notes = notes == null ? List.of() : List.copyOf(notes);
|
||||
}
|
||||
|
||||
public UnifiedRuntimeDerivedProjectionResultDto(
|
||||
UnifiedRuntimeProcessingRequest request,
|
||||
int driverSeedEventCount,
|
||||
int discoveredVehicleCount,
|
||||
int expandedVehicleEventCount,
|
||||
int mergedEventCount,
|
||||
List<UnifiedDiscoveredVehicleRef> discoveredVehicles,
|
||||
TachographEsperDriverProcessingResultDto projection,
|
||||
List<String> notes
|
||||
) {
|
||||
this(
|
||||
request,
|
||||
driverSeedEventCount,
|
||||
discoveredVehicleCount,
|
||||
expandedVehicleEventCount,
|
||||
mergedEventCount,
|
||||
discoveredVehicles,
|
||||
projection,
|
||||
notes,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public UnifiedRuntimeDerivedProjectionResultDto withPartitionDebug(RuntimeDriverPartitionDebugDto debug) {
|
||||
return new UnifiedRuntimeDerivedProjectionResultDto(
|
||||
request,
|
||||
driverSeedEventCount,
|
||||
discoveredVehicleCount,
|
||||
expandedVehicleEventCount,
|
||||
mergedEventCount,
|
||||
discoveredVehicles,
|
||||
projection,
|
||||
notes,
|
||||
debug
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,14 @@ public record UnifiedRuntimeTachographEsperScopeResultDto(
|
|||
int discoveredVehicleCount,
|
||||
List<UnifiedDiscoveredVehicleRef> discoveredVehicles,
|
||||
Map<String, UnifiedRuntimeDerivedProjectionResultDto> driverResults,
|
||||
Map<String, RuntimeDriverPartitionDebugDto> partitionDebugByDriver,
|
||||
List<String> notes,
|
||||
List<String> warnings
|
||||
) {
|
||||
public UnifiedRuntimeTachographEsperScopeResultDto {
|
||||
discoveredVehicles = discoveredVehicles == null ? List.of() : List.copyOf(discoveredVehicles);
|
||||
driverResults = driverResults == null ? Map.of() : Collections.unmodifiableMap(new LinkedHashMap<>(driverResults));
|
||||
partitionDebugByDriver = partitionDebugByDriver == null ? Map.of() : Collections.unmodifiableMap(new LinkedHashMap<>(partitionDebugByDriver));
|
||||
notes = notes == null ? List.of() : List.copyOf(notes);
|
||||
warnings = warnings == null ? List.of() : List.copyOf(warnings);
|
||||
}
|
||||
|
|
@ -50,9 +52,23 @@ public record UnifiedRuntimeTachographEsperScopeResultDto(
|
|||
genericResult.discoveredVehicleCount(),
|
||||
genericResult.discoveredVehicles(),
|
||||
driverResults,
|
||||
extractPartitionDebug(genericResult),
|
||||
genericResult.notes(),
|
||||
genericResult.warnings()
|
||||
);
|
||||
}
|
||||
|
||||
private static Map<String, RuntimeDriverPartitionDebugDto> extractPartitionDebug(
|
||||
RuntimeEventProcessingResultDto genericResult
|
||||
) {
|
||||
LinkedHashMap<String, RuntimeDriverPartitionDebugDto> debugByDriver = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, RuntimeEventProcessingPartitionResultDto> entry : genericResult.partitionResults().entrySet()) {
|
||||
Object debug = entry.getValue().metadata().get("partitionDebug");
|
||||
if (debug instanceof RuntimeDriverPartitionDebugDto partitionDebug) {
|
||||
debugByDriver.put(entry.getKey(), partitionDebug);
|
||||
}
|
||||
}
|
||||
return debugByDriver;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ public record RuntimeEventPartitioningApiRequest(
|
|||
Set<String> vehicleKeys,
|
||||
Boolean includeAllVehicles,
|
||||
Boolean attachVehicleEvidence,
|
||||
Integer vehicleEvidencePaddingMinutes
|
||||
Integer vehicleEvidencePaddingMinutes,
|
||||
Boolean includeDebug
|
||||
) {
|
||||
public RuntimeEventPartitioningApiRequest {
|
||||
strategy = strategy == null ? RuntimeEventPartitioningStrategy.CUSTOM_PROFILE : strategy;
|
||||
|
|
@ -43,4 +44,8 @@ public record RuntimeEventPartitioningApiRequest(
|
|||
public int vehicleEvidencePaddingMinutesOrDefault(int fallback) {
|
||||
return vehicleEvidencePaddingMinutes == null ? Math.max(0, fallback) : Math.max(0, vehicleEvidencePaddingMinutes);
|
||||
}
|
||||
|
||||
public boolean includeDebugOrDefault() {
|
||||
return includeDebug != null && includeDebug;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public record RuntimeEventProcessingApiRequest(
|
|||
throw new IllegalArgumentException("scope must not be null");
|
||||
}
|
||||
partitioning = partitioning == null
|
||||
? new RuntimeEventPartitioningApiRequest(null, null, null, null, null, null, null, null, null)
|
||||
? new RuntimeEventPartitioningApiRequest(null, null, null, null, null, null, null, null, null, null)
|
||||
: partitioning;
|
||||
parameters = parameters == null
|
||||
? Map.of()
|
||||
|
|
@ -40,7 +40,8 @@ public record RuntimeEventProcessingApiRequest(
|
|||
scope != null ? scope.vehicleKeys() : null,
|
||||
scope != null ? scope.includeAllVehicles() : null,
|
||||
null,
|
||||
scope != null ? scope.vehicleExpansionPaddingMinutes() : null
|
||||
scope != null ? scope.vehicleExpansionPaddingMinutes() : null,
|
||||
null
|
||||
),
|
||||
Map.of()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -55,30 +55,49 @@ public class TachographDriverEsperRuntimeEventProcessingProfile implements Runti
|
|||
|
||||
@Override
|
||||
public Set<String> optionalParameters() {
|
||||
return Set.of("significantDrivingMinutes", "minimumRestPeriodMinutes", "attachVehicleOnlyEvents", "vehicleEvidencePaddingMinutes");
|
||||
return Set.of(
|
||||
"significantDrivingMinutes",
|
||||
"minimumRestPeriodMinutes",
|
||||
"attachVehicleOnlyEvents",
|
||||
"vehicleEvidencePaddingMinutes",
|
||||
"includePartitionDebug"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeEventProcessingResultDto process(RuntimeEventProcessingApiRequest request) {
|
||||
boolean includePartitionDebug = booleanParameter(
|
||||
request.parameters(),
|
||||
"includePartitionDebug",
|
||||
request.partitioning() != null && request.partitioning().includeDebugOrDefault()
|
||||
);
|
||||
UnifiedRuntimeProcessingApiRequest tachographScopeRequest = applyGenericRequest(request.scope(), request.partitioning(), request.parameters());
|
||||
UnifiedRuntimeTachographEsperScopeResultDto tachographResult = tachographScopeProcessingService.processScope(tachographScopeRequest);
|
||||
UnifiedRuntimeTachographEsperScopeResultDto tachographResult = tachographScopeProcessingService.processScope(
|
||||
tachographScopeRequest,
|
||||
includePartitionDebug
|
||||
);
|
||||
|
||||
Map<String, RuntimeEventProcessingPartitionResultDto> partitionResults = new LinkedHashMap<>();
|
||||
tachographResult.driverResults().forEach((driverKey, driverResult) -> partitionResults.put(
|
||||
driverKey,
|
||||
new RuntimeEventProcessingPartitionResultDto(
|
||||
"DRIVER",
|
||||
driverKey,
|
||||
"UnifiedRuntimeDerivedProjectionResultDto",
|
||||
driverResult,
|
||||
Map.of(
|
||||
"projectionResultType", driverResult.projection() == null ? "NONE" : "TachographEsperDriverProcessingResultDto",
|
||||
"driverSeedEventCount", driverResult.driverSeedEventCount(),
|
||||
"expandedVehicleEventCount", driverResult.expandedVehicleEventCount(),
|
||||
"mergedEventCount", driverResult.mergedEventCount()
|
||||
)
|
||||
)
|
||||
));
|
||||
tachographResult.driverResults().forEach((driverKey, driverResult) -> {
|
||||
Map<String, Object> metadata = new LinkedHashMap<>();
|
||||
metadata.put("projectionResultType", driverResult.projection() == null ? "NONE" : "TachographEsperDriverProcessingResultDto");
|
||||
metadata.put("driverSeedEventCount", driverResult.driverSeedEventCount());
|
||||
metadata.put("expandedVehicleEventCount", driverResult.expandedVehicleEventCount());
|
||||
metadata.put("mergedEventCount", driverResult.mergedEventCount());
|
||||
if (driverResult.partitionDebug() != null) {
|
||||
metadata.put("partitionDebug", driverResult.partitionDebug());
|
||||
}
|
||||
partitionResults.put(
|
||||
driverKey,
|
||||
new RuntimeEventProcessingPartitionResultDto(
|
||||
"DRIVER",
|
||||
driverKey,
|
||||
"UnifiedRuntimeDerivedProjectionResultDto",
|
||||
driverResult,
|
||||
metadata
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return new RuntimeEventProcessingResultDto(
|
||||
profileKey(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package at.procon.eventhub.processing.model;
|
||||
|
||||
import at.procon.eventhub.dto.EventHubEventDto;
|
||||
import at.procon.eventhub.processing.dto.RuntimeDriverPartitionDebugDto;
|
||||
import at.procon.eventhub.processing.dto.RuntimeVehicleEvidenceAttachmentDecisionDto;
|
||||
import at.procon.eventhub.processing.dto.RuntimeVehicleUsageIntervalDebugDto;
|
||||
import java.util.List;
|
||||
|
||||
public record RuntimeDriverVehicleEvidenceAttachmentResult(
|
||||
|
|
@ -11,6 +14,8 @@ public record RuntimeDriverVehicleEvidenceAttachmentResult(
|
|||
int vehicleUsageIntervalCount,
|
||||
int candidateVehicleEvidenceEventCount,
|
||||
int ignoredVehicleEvidenceEventCount,
|
||||
List<RuntimeVehicleUsageIntervalDebugDto> vehicleUsageIntervals,
|
||||
List<RuntimeVehicleEvidenceAttachmentDecisionDto> vehicleEvidenceDecisions,
|
||||
List<String> notes,
|
||||
List<String> warnings
|
||||
) {
|
||||
|
|
@ -18,7 +23,25 @@ public record RuntimeDriverVehicleEvidenceAttachmentResult(
|
|||
directDriverEvents = directDriverEvents == null ? List.of() : List.copyOf(directDriverEvents);
|
||||
attachedVehicleEvidenceEvents = attachedVehicleEvidenceEvents == null ? List.of() : List.copyOf(attachedVehicleEvidenceEvents);
|
||||
mergedEvents = mergedEvents == null ? List.of() : List.copyOf(mergedEvents);
|
||||
vehicleUsageIntervals = vehicleUsageIntervals == null ? List.of() : List.copyOf(vehicleUsageIntervals);
|
||||
vehicleEvidenceDecisions = vehicleEvidenceDecisions == null ? List.of() : List.copyOf(vehicleEvidenceDecisions);
|
||||
notes = notes == null ? List.of() : List.copyOf(notes);
|
||||
warnings = warnings == null ? List.of() : List.copyOf(warnings);
|
||||
}
|
||||
|
||||
public RuntimeDriverPartitionDebugDto toPartitionDebug() {
|
||||
return new RuntimeDriverPartitionDebugDto(
|
||||
driverKey,
|
||||
directDriverEvents.size(),
|
||||
vehicleUsageIntervalCount,
|
||||
candidateVehicleEvidenceEventCount,
|
||||
attachedVehicleEvidenceEvents.size(),
|
||||
ignoredVehicleEvidenceEventCount,
|
||||
mergedEvents.size(),
|
||||
vehicleUsageIntervals,
|
||||
vehicleEvidenceDecisions,
|
||||
notes,
|
||||
warnings
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package at.procon.eventhub.processing.service;
|
|||
|
||||
import at.procon.eventhub.dto.EventHubEventDto;
|
||||
import at.procon.eventhub.dto.VehicleRefDto;
|
||||
import at.procon.eventhub.processing.dto.RuntimeVehicleEvidenceAttachmentDecisionDto;
|
||||
import at.procon.eventhub.processing.dto.RuntimeVehicleUsageIntervalDebugDto;
|
||||
import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeClassifier;
|
||||
import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeType;
|
||||
import at.procon.eventhub.processing.model.RuntimeDriverVehicleEvidenceAttachmentResult;
|
||||
|
|
@ -38,6 +40,24 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
|
|||
List<EventHubEventDto> runtimeScopeEvents,
|
||||
boolean attachVehicleOnlyEvents,
|
||||
int vehicleEvidencePaddingMinutes
|
||||
) {
|
||||
return attachVehicleEvidence(
|
||||
driverKey,
|
||||
directDriverEvents,
|
||||
runtimeScopeEvents,
|
||||
attachVehicleOnlyEvents,
|
||||
vehicleEvidencePaddingMinutes,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public RuntimeDriverVehicleEvidenceAttachmentResult attachVehicleEvidence(
|
||||
String driverKey,
|
||||
List<EventHubEventDto> directDriverEvents,
|
||||
List<EventHubEventDto> runtimeScopeEvents,
|
||||
boolean attachVehicleOnlyEvents,
|
||||
int vehicleEvidencePaddingMinutes,
|
||||
boolean includeDebug
|
||||
) {
|
||||
List<EventHubEventDto> safeDriverEvents = directDriverEvents == null ? List.of() : List.copyOf(directDriverEvents);
|
||||
List<EventHubEventDto> safeScopeEvents = runtimeScopeEvents == null ? List.of() : List.copyOf(runtimeScopeEvents);
|
||||
|
|
@ -47,14 +67,19 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
|
|||
List<String> warnings = new ArrayList<>();
|
||||
if (!attachVehicleOnlyEvents) {
|
||||
notes.add("Vehicle-only evidence attachment is disabled for driver partition " + driverKey + ".");
|
||||
List<RuntimeVehicleEvidenceAttachmentDecisionDto> disabledDecisions = includeDebug
|
||||
? disabledDecisions(safeScopeEvents)
|
||||
: List.of();
|
||||
return new RuntimeDriverVehicleEvidenceAttachmentResult(
|
||||
driverKey,
|
||||
safeDriverEvents,
|
||||
List.of(),
|
||||
deduplicateAndSort(safeDriverEvents, List.of()),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
disabledDecisions.size(),
|
||||
disabledDecisions.size(),
|
||||
List.of(),
|
||||
disabledDecisions,
|
||||
notes,
|
||||
warnings
|
||||
);
|
||||
|
|
@ -62,6 +87,12 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
|
|||
|
||||
ResolvedDriverTimeline timeline = timelineReconstructor.reconstruct(null, driverKey, safeDriverEvents);
|
||||
List<ResolvedVehicleUsageInterval> usageIntervals = mergeVehicleUsageIntervals(timeline.vehicleUsageIntervals());
|
||||
List<RuntimeVehicleUsageIntervalDebugDto> usageIntervalDebug = includeDebug
|
||||
? usageIntervals.stream().map(RuntimeVehicleUsageIntervalDebugDto::from).toList()
|
||||
: List.of();
|
||||
List<RuntimeVehicleEvidenceAttachmentDecisionDto> decisions = includeDebug
|
||||
? directDriverDecisions(safeDriverEvents)
|
||||
: new ArrayList<>();
|
||||
List<EventHubEventDto> candidateVehicleEvidence = safeScopeEvents.stream()
|
||||
.filter(event -> scopeClassifier.classify(event) == RuntimeEventScopeType.VEHICLE_SCOPED)
|
||||
.toList();
|
||||
|
|
@ -71,9 +102,25 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
|
|||
List<ResolvedVehicleUsageInterval> matchingIntervals = matchingUsageIntervals(vehicleEvent, usageIntervals, paddingMinutes);
|
||||
if (matchingIntervals.isEmpty()) {
|
||||
ignored++;
|
||||
if (includeDebug) {
|
||||
decisions.add(decision(
|
||||
"IGNORED_NO_OVERLAPPING_VEHICLE_USAGE",
|
||||
"Vehicle-scoped event did not overlap any reconstructed vehicle-usage interval for driver " + driverKey + ".",
|
||||
vehicleEvent,
|
||||
List.of()
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
attached.add(vehicleEvent);
|
||||
if (includeDebug) {
|
||||
decisions.add(decision(
|
||||
"ATTACHED_VEHICLE_EVIDENCE",
|
||||
"Vehicle-scoped event overlapped driver vehicle usage interval(s).",
|
||||
vehicleEvent,
|
||||
matchingIntervals
|
||||
));
|
||||
}
|
||||
if (matchingIntervals.size() > 1) {
|
||||
warnings.add("Vehicle-only event " + vehicleEvent.externalSourceEventId()
|
||||
+ " matched multiple vehicle-usage intervals for driver " + driverKey
|
||||
|
|
@ -100,11 +147,61 @@ public class RuntimeDriverVehicleEvidenceAttachmentService {
|
|||
usageIntervals.size(),
|
||||
candidateVehicleEvidence.size(),
|
||||
ignored,
|
||||
usageIntervalDebug,
|
||||
includeDebug ? decisions : List.of(),
|
||||
notes,
|
||||
warnings
|
||||
);
|
||||
}
|
||||
|
||||
private List<RuntimeVehicleEvidenceAttachmentDecisionDto> disabledDecisions(List<EventHubEventDto> runtimeScopeEvents) {
|
||||
return (runtimeScopeEvents == null ? List.<EventHubEventDto>of() : runtimeScopeEvents).stream()
|
||||
.filter(event -> scopeClassifier.classify(event) == RuntimeEventScopeType.VEHICLE_SCOPED)
|
||||
.map(event -> decision(
|
||||
"IGNORED_ATTACHMENT_DISABLED",
|
||||
"Vehicle evidence attachment was disabled for this partition.",
|
||||
event,
|
||||
List.of()
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<RuntimeVehicleEvidenceAttachmentDecisionDto> directDriverDecisions(List<EventHubEventDto> directDriverEvents) {
|
||||
return (directDriverEvents == null ? List.<EventHubEventDto>of() : directDriverEvents).stream()
|
||||
.map(event -> decision(
|
||||
"DIRECT_DRIVER_EVENT",
|
||||
"Event already carries the selected driver reference and belongs directly to the driver partition.",
|
||||
event,
|
||||
List.of()
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private RuntimeVehicleEvidenceAttachmentDecisionDto decision(
|
||||
String decision,
|
||||
String reason,
|
||||
EventHubEventDto event,
|
||||
List<ResolvedVehicleUsageInterval> matchingIntervals
|
||||
) {
|
||||
List<String> intervalIds = (matchingIntervals == null ? List.<ResolvedVehicleUsageInterval>of() : matchingIntervals).stream()
|
||||
.map(ResolvedVehicleUsageInterval::intervalId)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
return new RuntimeVehicleEvidenceAttachmentDecisionDto(
|
||||
decision,
|
||||
reason,
|
||||
dedupKey(event),
|
||||
event == null ? null : event.externalSourceEventId(),
|
||||
event == null ? null : event.occurredAt(),
|
||||
event == null || event.eventDomain() == null ? null : event.eventDomain().name(),
|
||||
event == null || event.eventType() == null ? null : event.eventType().name(),
|
||||
event == null || event.lifecycle() == null ? null : event.lifecycle().name(),
|
||||
scopeClassifier.classify(event),
|
||||
vehicleKeys(event),
|
||||
intervalIds
|
||||
);
|
||||
}
|
||||
|
||||
private List<ResolvedVehicleUsageInterval> matchingUsageIntervals(
|
||||
EventHubEventDto vehicleEvent,
|
||||
List<ResolvedVehicleUsageInterval> usageIntervals,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package at.procon.eventhub.processing.service;
|
|||
import at.procon.eventhub.dto.DriverRefDto;
|
||||
import at.procon.eventhub.dto.EventHubEventDto;
|
||||
import at.procon.eventhub.dto.VehicleRefDto;
|
||||
import at.procon.eventhub.processing.dto.RuntimeDriverPartitionDebugDto;
|
||||
import at.procon.eventhub.processing.dto.UnifiedRuntimeDerivedProjectionResultDto;
|
||||
import at.procon.eventhub.processing.dto.UnifiedRuntimeProcessingApiRequest;
|
||||
import at.procon.eventhub.processing.dto.UnifiedRuntimeTachographEsperScopeResultDto;
|
||||
|
|
@ -39,6 +40,13 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
}
|
||||
|
||||
public UnifiedRuntimeTachographEsperScopeResultDto processScope(UnifiedRuntimeProcessingApiRequest apiRequest) {
|
||||
return processScope(apiRequest, false);
|
||||
}
|
||||
|
||||
public UnifiedRuntimeTachographEsperScopeResultDto processScope(
|
||||
UnifiedRuntimeProcessingApiRequest apiRequest,
|
||||
boolean includePartitionDebug
|
||||
) {
|
||||
UnifiedRuntimeProcessingRequest request = apiRequest.toRuntimeRequest();
|
||||
UnifiedRuntimeEventBundle broadBundle = eventAssemblyService.assembleDriverScopedEvents(request);
|
||||
LinkedHashSet<String> selectedDriverKeys = selectedDriverKeys(request, broadBundle.mergedEvents());
|
||||
|
|
@ -47,10 +55,15 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
}
|
||||
|
||||
Map<String, UnifiedRuntimeDerivedProjectionResultDto> driverResults = new LinkedHashMap<>();
|
||||
Map<String, RuntimeDriverPartitionDebugDto> partitionDebugByDriver = new LinkedHashMap<>();
|
||||
Map<String, List<String>> attachedVehicleEvidenceByEvent = new LinkedHashMap<>();
|
||||
List<String> warnings = new ArrayList<>();
|
||||
for (String driverKey : selectedDriverKeys) {
|
||||
UnifiedRuntimeEventBundle driverBundle = partitionForDriver(request, broadBundle, driverKey);
|
||||
DriverPartition driverPartition = partitionForDriver(request, broadBundle, driverKey, includePartitionDebug);
|
||||
UnifiedRuntimeEventBundle driverBundle = driverPartition.bundle();
|
||||
if (driverPartition.debug() != null) {
|
||||
partitionDebugByDriver.put(driverKey, driverPartition.debug());
|
||||
}
|
||||
for (EventHubEventDto attachedEvent : driverBundle.expandedVehicleEvents()) {
|
||||
attachedVehicleEvidenceByEvent
|
||||
.computeIfAbsent(dedupKey(attachedEvent), ignored -> new ArrayList<>())
|
||||
|
|
@ -71,7 +84,7 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
driverBundle,
|
||||
driverKey
|
||||
);
|
||||
driverResults.put(driverKey, driverResult);
|
||||
driverResults.put(driverKey, driverPartition.debug() == null ? driverResult : driverResult.withPartitionDebug(driverPartition.debug()));
|
||||
}
|
||||
attachedVehicleEvidenceByEvent.forEach((eventKey, drivers) -> {
|
||||
if (drivers.size() > 1) {
|
||||
|
|
@ -98,6 +111,7 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
broadBundle.discoveredVehicles().size(),
|
||||
broadBundle.discoveredVehicles(),
|
||||
driverResults,
|
||||
partitionDebugByDriver,
|
||||
notes,
|
||||
warnings
|
||||
);
|
||||
|
|
@ -130,10 +144,11 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
return selected;
|
||||
}
|
||||
|
||||
private UnifiedRuntimeEventBundle partitionForDriver(
|
||||
private DriverPartition partitionForDriver(
|
||||
UnifiedRuntimeProcessingRequest request,
|
||||
UnifiedRuntimeEventBundle broadBundle,
|
||||
String driverKey
|
||||
String driverKey,
|
||||
boolean includePartitionDebug
|
||||
) {
|
||||
List<EventHubEventDto> directDriverEvents = broadBundle.mergedEvents().stream()
|
||||
.filter(event -> Objects.equals(driverKey(event), driverKey))
|
||||
|
|
@ -143,7 +158,8 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
directDriverEvents,
|
||||
broadBundle.mergedEvents(),
|
||||
request.expandVehicleEvents(),
|
||||
request.vehicleExpansionPaddingMinutes()
|
||||
request.vehicleExpansionPaddingMinutes(),
|
||||
includePartitionDebug
|
||||
);
|
||||
List<UnifiedDiscoveredVehicleRef> driverVehicles = discoverVehicles(attachmentResult.mergedEvents());
|
||||
List<String> notes = new ArrayList<>(broadBundle.notes());
|
||||
|
|
@ -153,7 +169,7 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
notes.add("Vehicle-usage intervals used for temporal evidence attachment: " + attachmentResult.vehicleUsageIntervalCount() + ".");
|
||||
notes.addAll(attachmentResult.notes());
|
||||
attachmentResult.warnings().forEach(warning -> notes.add("WARNING: " + warning));
|
||||
return new UnifiedRuntimeEventBundle(
|
||||
UnifiedRuntimeEventBundle bundle = new UnifiedRuntimeEventBundle(
|
||||
request.withDriverKey(driverKey),
|
||||
attachmentResult.directDriverEvents(),
|
||||
driverVehicles,
|
||||
|
|
@ -161,6 +177,16 @@ public class UnifiedRuntimeTachographEsperScopeProcessingService {
|
|||
attachmentResult.mergedEvents(),
|
||||
notes
|
||||
);
|
||||
return new DriverPartition(
|
||||
bundle,
|
||||
includePartitionDebug ? attachmentResult.toPartitionDebug() : null
|
||||
);
|
||||
}
|
||||
|
||||
private record DriverPartition(
|
||||
UnifiedRuntimeEventBundle bundle,
|
||||
RuntimeDriverPartitionDebugDto debug
|
||||
) {
|
||||
}
|
||||
|
||||
private LinkedHashSet<String> discoverDriverKeys(List<EventHubEventDto> events) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class RuntimeEventProcessingServiceTest {
|
|||
null,
|
||||
null
|
||||
),
|
||||
new RuntimeEventPartitioningApiRequest(RuntimeEventPartitioningStrategy.DRIVER, null, false, null, false, null, false, null, null),
|
||||
new RuntimeEventPartitioningApiRequest(RuntimeEventPartitioningStrategy.DRIVER, null, false, null, false, null, false, null, null, null),
|
||||
Map.of()
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package at.procon.eventhub.processing.eventprocessing.profile;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
|
@ -34,7 +35,13 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
|||
assertThat(profile.displayName()).isEqualTo("Tachograph Driver Esper Processing");
|
||||
assertThat(profile.defaultPartitioningStrategy()).isEqualTo(RuntimeEventPartitioningStrategy.DRIVER);
|
||||
assertThat(profile.supportedPartitioningStrategies()).containsExactly(RuntimeEventPartitioningStrategy.DRIVER);
|
||||
assertThat(profile.optionalParameters()).containsExactlyInAnyOrder("significantDrivingMinutes", "minimumRestPeriodMinutes", "attachVehicleOnlyEvents", "vehicleEvidencePaddingMinutes");
|
||||
assertThat(profile.optionalParameters()).containsExactlyInAnyOrder(
|
||||
"significantDrivingMinutes",
|
||||
"minimumRestPeriodMinutes",
|
||||
"attachVehicleOnlyEvents",
|
||||
"vehicleEvidencePaddingMinutes",
|
||||
"includePartitionDebug"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -77,13 +84,15 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
Map.of(
|
||||
"significantDrivingMinutes", 5,
|
||||
"minimumRestPeriodMinutes", "600",
|
||||
"vehicleEvidencePaddingMinutes", 20,
|
||||
"attachVehicleOnlyEvents", true
|
||||
"attachVehicleOnlyEvents", true,
|
||||
"includePartitionDebug", true
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -117,7 +126,7 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
|||
null,
|
||||
List.of("driver processed")
|
||||
);
|
||||
when(scopeService.processScope(any()))
|
||||
when(scopeService.processScope(any(), anyBoolean()))
|
||||
.thenReturn(new UnifiedRuntimeTachographEsperScopeResultDto(
|
||||
processedRequest,
|
||||
5,
|
||||
|
|
@ -125,6 +134,7 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
|||
1,
|
||||
List.of(),
|
||||
Map.of("12:DRIVER-1", driverResult),
|
||||
Map.of(),
|
||||
List.of("scope processed"),
|
||||
List.of()
|
||||
));
|
||||
|
|
@ -138,12 +148,14 @@ class TachographDriverEsperRuntimeEventProcessingProfileTest {
|
|||
assertThat(result.partitionResults().get("12:DRIVER-1").result()).isSameAs(driverResult);
|
||||
|
||||
ArgumentCaptor<UnifiedRuntimeProcessingApiRequest> captor = ArgumentCaptor.forClass(UnifiedRuntimeProcessingApiRequest.class);
|
||||
verify(scopeService).processScope(captor.capture());
|
||||
ArgumentCaptor<Boolean> debugCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||
verify(scopeService).processScope(captor.capture(), debugCaptor.capture());
|
||||
UnifiedRuntimeProcessingApiRequest delegated = captor.getValue();
|
||||
assertThat(delegated.driverKeys()).containsExactly("12:DRIVER-1");
|
||||
assertThat(delegated.significantDrivingMinutes()).isEqualTo(5);
|
||||
assertThat(delegated.minimumRestPeriodMinutes()).isEqualTo(600);
|
||||
assertThat(delegated.vehicleExpansionPaddingMinutes()).isEqualTo(20);
|
||||
assertThat(delegated.expandVehicleEvents()).isTrue();
|
||||
assertThat(debugCaptor.getValue()).isTrue();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ 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.dto.RuntimeVehicleEvidenceAttachmentDecisionDto;
|
||||
import at.procon.eventhub.processing.eventprocessing.partition.RuntimeEventScopeClassifier;
|
||||
import at.procon.eventhub.processing.model.RuntimeDriverVehicleEvidenceAttachmentResult;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
|
@ -119,6 +120,34 @@ class RuntimeDriverVehicleEvidenceAttachmentServiceTest {
|
|||
.containsExactly("pos-midnight");
|
||||
}
|
||||
|
||||
@Test
|
||||
void producesDebugDecisionsForDirectAttachedAndIgnoredEvents() {
|
||||
List<EventHubEventDto> driverEvents = List.of(
|
||||
cardEvent("card-1-in", EventType.CARD_INSERTED, "DRIVER-1", "interval-1", "VIN-1", "AT:W-1", "2026-05-01T08:00:00Z"),
|
||||
cardEvent("card-1-out", EventType.CARD_WITHDRAWN, "DRIVER-1", "interval-1", "VIN-1", "AT:W-1", "2026-05-01T18:00:00Z")
|
||||
);
|
||||
EventHubEventDto inside = vehicleOnlyEvent("pos-inside", "VIN-1", "AT:W-1", "2026-05-01T12:00:00Z");
|
||||
EventHubEventDto outside = vehicleOnlyEvent("pos-outside", "VIN-1", "AT:W-1", "2026-05-01T20:00:00Z");
|
||||
|
||||
RuntimeDriverVehicleEvidenceAttachmentResult result = service.attachVehicleEvidence(
|
||||
"DRIVER-1",
|
||||
driverEvents,
|
||||
List.of(driverEvents.get(0), driverEvents.get(1), inside, outside),
|
||||
true,
|
||||
0,
|
||||
true
|
||||
);
|
||||
|
||||
assertThat(result.vehicleUsageIntervals()).hasSize(1);
|
||||
assertThat(result.vehicleEvidenceDecisions()).extracting(RuntimeVehicleEvidenceAttachmentDecisionDto::decision)
|
||||
.contains(
|
||||
"DIRECT_DRIVER_EVENT",
|
||||
"ATTACHED_VEHICLE_EVIDENCE",
|
||||
"IGNORED_NO_OVERLAPPING_VEHICLE_USAGE"
|
||||
);
|
||||
assertThat(result.toPartitionDebug().vehicleEvidenceDecisions()).hasSameSizeAs(result.vehicleEvidenceDecisions());
|
||||
}
|
||||
|
||||
private EventHubEventDto cardEvent(
|
||||
String externalId,
|
||||
EventType eventType,
|
||||
|
|
|
|||
Loading…
Reference in New Issue