diff --git a/src/main/java/at/procon/eventhub/config/EventHubProperties.java b/src/main/java/at/procon/eventhub/config/EventHubProperties.java index 2987477..8305d74 100644 --- a/src/main/java/at/procon/eventhub/config/EventHubProperties.java +++ b/src/main/java/at/procon/eventhub/config/EventHubProperties.java @@ -351,19 +351,21 @@ public class EventHubProperties { } public static class LegalRequirements { - private String baseUrl = "https://legalrequirements.services.bytebar.eu/ODataV4/LR"; + private static final String DEFAULT_BASE_URL = "https://legalrequirements.services.bytebar.eu/ODataV4/LR"; + + private String baseUrl = DEFAULT_BASE_URL; private String username; private String password; private Duration connectTimeout = Duration.ofSeconds(20); private Duration readTimeout = Duration.ofMinutes(2); - private boolean resetSessionAfterUse = true; + private boolean resetSessionAfterUse = false; public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String baseUrl) { - this.baseUrl = baseUrl; + this.baseUrl = normalizeBaseUrl(baseUrl); } public String getUsername() { @@ -409,6 +411,22 @@ public class EventHubProperties { public void setResetSessionAfterUse(boolean resetSessionAfterUse) { this.resetSessionAfterUse = resetSessionAfterUse; } + + private String normalizeBaseUrl(String baseUrl) { + if (baseUrl == null || baseUrl.isBlank()) { + return DEFAULT_BASE_URL; + } + + String normalized = baseUrl.trim(); + while (normalized.startsWith(":")) { + normalized = normalized.substring(1).trim(); + } + while (normalized.endsWith("/")) { + normalized = normalized.substring(0, normalized.length() - 1).trim(); + } + + return normalized.isBlank() ? DEFAULT_BASE_URL : normalized; + } } public static class TachographDataSource { diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java index 462ab1d..0a79c9f 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java @@ -4,6 +4,7 @@ import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInter import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval; import at.procon.eventhub.tachographfilesession.model.ExtractedDriver; import at.procon.eventhub.tachographfilesession.model.ExtractedDriverCard; +import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent; import at.procon.eventhub.tachographfilesession.model.ExtractedVehicle; import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration; import at.procon.eventhub.tachographfilesession.model.ExtractionWarning; @@ -19,6 +20,7 @@ public record TachographFileDriverDetailDto( List vehicles, List cardVehicleUsageIntervals, List cardActivityIntervals, + List supportEvents, List warnings ) { } diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java index 93aff39..fb0de47 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java @@ -10,6 +10,7 @@ public record DriverExtractionSession( List vehicles, List cardVehicleUsageIntervals, List cardActivityIntervals, + List supportEvents, List warnings ) { } diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java new file mode 100644 index 0000000..07def58 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedSupportEvent.java @@ -0,0 +1,23 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.math.BigDecimal; +import java.time.OffsetDateTime; + +public record ExtractedSupportEvent( + String eventId, + OffsetDateTime occurredAt, + String eventDomain, + String eventType, + String slot, + String registrationKey, + String vehicleKey, + String country, + String region, + BigDecimal latitude, + BigDecimal longitude, + String authenticationStatus, + Long odometerKm, + String code, + String rawRecordPath +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java index 322a612..1bc5902 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java @@ -87,6 +87,7 @@ public class DriverCardXmlExtractionService { List.copyOf(vehiclesByKey.values()), List.copyOf(vehicleUsageIntervals), List.copyOf(activityIntervals), + List.of(), List.copyOf(warnings) ); Map driversByKey = Map.of(driverKey, driverSession); diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java index 5f89bb7..549b9ff 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java @@ -111,6 +111,7 @@ public class TachographFileSessionService { driver.vehicles(), driver.cardVehicleUsageIntervals(), driver.cardActivityIntervals(), + driver.supportEvents(), driver.warnings() ); } diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java index 23a5c0c..d86f2dc 100644 --- a/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionService.java @@ -5,12 +5,14 @@ import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInter import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval; import at.procon.eventhub.tachographfilesession.model.ExtractedDriver; import at.procon.eventhub.tachographfilesession.model.ExtractedDriverCard; +import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent; import at.procon.eventhub.tachographfilesession.model.ExtractedVehicle; import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration; import at.procon.eventhub.tachographfilesession.model.ExtractionStats; import at.procon.eventhub.tachographfilesession.model.ExtractionWarning; import at.procon.eventhub.tachographfilesession.model.TachographFileSession; import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata; +import java.math.BigDecimal; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; @@ -150,6 +152,7 @@ public class VehicleUnitXmlExtractionService { List vuActivityIntervals = extractActivityIntervals(document, vehicleContext, sessionWarnings); assignActivityCoverage(vuActivityIntervals, vuCardIwIntervals, vehicleContext, driversByKey, sessionWarnings); + extractSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, sessionWarnings); List allWarnings = new ArrayList<>(sessionWarnings); Map driverSessions = new LinkedHashMap<>(); @@ -369,6 +372,205 @@ public class VehicleUnitXmlExtractionService { } } + private void extractSupportEvents( + Document document, + VehicleContext vehicleContext, + List vuCardIwIntervals, + Map driversByKey, + List warnings + ) { + extractVuPlaceSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, warnings); + extractVuGnssSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, warnings); + extractVuSpecificConditionSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, warnings); + } + + private void extractVuPlaceSupportEvents( + Document document, + VehicleContext vehicleContext, + List vuCardIwIntervals, + Map driversByKey, + List warnings + ) { + NodeList records = nodes(document, "/VehicleUnit/Activities/vuPlaceDailyWorkPeriodData/vuPlaceDailyWorkPeriodRecords"); + for (int i = 0; i < records.getLength(); i++) { + Element record = (Element) records.item(i); + String path = "/VehicleUnit/Activities/vuPlaceDailyWorkPeriodData/vuPlaceDailyWorkPeriodRecords[" + (i + 1) + "]"; + OffsetDateTime occurredAt = offsetDateTime(text(record, "placeRecord/entryTime")); + if (occurredAt == null) { + warnings.add(new ExtractionWarning("VU_PLACE_MISSING_TIME", "Vehicle-unit place record is missing entryTime.", path)); + continue; + } + + String entryType = text(record, "placeRecord/entryTypeDailyWorkPeriod"); + List assignments = resolveDriverAssignments( + occurredAt, + driverKeyFromCardNode(record, "fullCardNumber"), + null, + vuCardIwIntervals + ); + if (assignments.isEmpty()) { + warnings.add(new ExtractionWarning("VU_PLACE_UNASSIGNED", "Vehicle-unit place record could not be assigned to a driver session.", path)); + continue; + } + if (assignments.size() > 1) { + warnings.add(new ExtractionWarning("VU_PLACE_AMBIGUOUS", "Vehicle-unit place record matched multiple active driver sessions.", path)); + } + + BigDecimal latitude = geoCoordinate(record, "placeRecord/entryGnssPlaceRecord/geoCoordinates/latitude", true); + BigDecimal longitude = geoCoordinate(record, "placeRecord/entryGnssPlaceRecord/geoCoordinates/longitude", false); + String authenticationStatus = normalizeToken(text(record, "placeRecord/entryGnssPlaceRecord/authenticationStatus")); + for (DriverAssignment assignment : assignments) { + addSupportEvent( + driversByKey, + assignment, + new ExtractedSupportEvent( + "VUPLACE-" + (i + 1) + "-" + assignment.driverKey(), + occurredAt, + "PLACE", + mapPlaceEntryType(entryType), + assignment.slot(), + vehicleContext.registration() == null ? null : vehicleContext.registration().registrationKey(), + vehicleContext.vehicle() == null ? null : vehicleContext.vehicle().vehicleKey(), + text(record, "placeRecord/dailyWorkPeriodCountry"), + text(record, "placeRecord/dailyWorkPeriodRegion"), + latitude, + longitude, + authenticationStatus, + longValue(text(record, "placeRecord/vehicleOdometerValue")), + normalizeToken(entryType), + path + ), + warnings + ); + } + } + } + + private void extractVuGnssSupportEvents( + Document document, + VehicleContext vehicleContext, + List vuCardIwIntervals, + Map driversByKey, + List warnings + ) { + NodeList records = nodes(document, "/VehicleUnit/Activities/vuGnssADData/vuGnssADRecord"); + for (int i = 0; i < records.getLength(); i++) { + Element record = (Element) records.item(i); + String path = "/VehicleUnit/Activities/vuGnssADData/vuGnssADRecord[" + (i + 1) + "]"; + OffsetDateTime occurredAt = offsetDateTime(text(record, "timeStamp")); + if (occurredAt == null) { + warnings.add(new ExtractionWarning("VU_GNSS_MISSING_TIME", "Vehicle-unit GNSS record is missing timeStamp.", path)); + continue; + } + + List assignments = new ArrayList<>(); + assignments.addAll(resolveDriverAssignments( + occurredAt, + driverKeyFromCardNode(record, "cardNumberDriverSlot"), + "DRIVER", + vuCardIwIntervals + )); + assignments.addAll(resolveDriverAssignments( + occurredAt, + driverKeyFromCardNode(record, "cardNumberCodriverSlot"), + "CO_DRIVER", + vuCardIwIntervals + )); + assignments = distinctAssignments(assignments); + if (assignments.isEmpty()) { + warnings.add(new ExtractionWarning("VU_GNSS_UNASSIGNED", "Vehicle-unit GNSS record could not be assigned to a driver session.", path)); + continue; + } + + BigDecimal latitude = geoCoordinate(record, "gnssPlaceRecord/geoCoordinates/latitude", true); + BigDecimal longitude = geoCoordinate(record, "gnssPlaceRecord/geoCoordinates/longitude", false); + String authenticationStatus = normalizeToken(text(record, "gnssPlaceRecord/authenticationStatus")); + for (DriverAssignment assignment : assignments) { + addSupportEvent( + driversByKey, + assignment, + new ExtractedSupportEvent( + "VUGNSS-" + (i + 1) + "-" + assignment.driverKey() + "-" + assignment.slot(), + occurredAt, + "POSITION", + "GNSS_ACCUMULATED_DRIVING", + assignment.slot(), + vehicleContext.registration() == null ? null : vehicleContext.registration().registrationKey(), + vehicleContext.vehicle() == null ? null : vehicleContext.vehicle().vehicleKey(), + null, + null, + latitude, + longitude, + authenticationStatus, + longValue(text(record, "vehicleOdometerValue")), + null, + path + ), + warnings + ); + } + } + } + + private void extractVuSpecificConditionSupportEvents( + Document document, + VehicleContext vehicleContext, + List vuCardIwIntervals, + Map driversByKey, + List warnings + ) { + NodeList records = nodes(document, "/VehicleUnit/Activities/vuSpecificConditionData/specificConditionRecords"); + for (int i = 0; i < records.getLength(); i++) { + Element record = (Element) records.item(i); + String path = "/VehicleUnit/Activities/vuSpecificConditionData/specificConditionRecords[" + (i + 1) + "]"; + OffsetDateTime occurredAt = offsetDateTime(text(record, "entryTime")); + if (occurredAt == null) { + warnings.add(new ExtractionWarning("VU_SPECIFIC_CONDITION_MISSING_TIME", "Vehicle-unit specific-condition record is missing entryTime.", path)); + continue; + } + + List assignments = distinctAssignments(resolveDriverAssignments( + occurredAt, + null, + null, + vuCardIwIntervals + )); + if (assignments.isEmpty()) { + warnings.add(new ExtractionWarning("VU_SPECIFIC_CONDITION_UNASSIGNED", "Vehicle-unit specific-condition record could not be assigned to an active driver session.", path)); + continue; + } + if (assignments.size() > 1) { + warnings.add(new ExtractionWarning("VU_SPECIFIC_CONDITION_AMBIGUOUS", "Vehicle-unit specific-condition record matched multiple active driver sessions.", path)); + } + + String conditionCode = normalizeToken(text(record, "specificConditionType")); + for (DriverAssignment assignment : assignments) { + addSupportEvent( + driversByKey, + assignment, + new ExtractedSupportEvent( + "VUSC-" + (i + 1) + "-" + assignment.driverKey() + "-" + assignment.slot(), + occurredAt, + "SPECIFIC_CONDITION", + "SPECIFIC_CONDITION", + assignment.slot(), + vehicleContext.registration() == null ? null : vehicleContext.registration().registrationKey(), + vehicleContext.vehicle() == null ? null : vehicleContext.vehicle().vehicleKey(), + null, + null, + null, + null, + null, + null, + conditionCode, + path + ), + warnings + ); + } + } + } + private boolean isPartiallyCovered(ExtractedCardActivityInterval interval, List segments) { if (segments.isEmpty()) { return true; @@ -429,6 +631,54 @@ public class VehicleUnitXmlExtractionService { return segments; } + private void addSupportEvent( + Map driversByKey, + DriverAssignment assignment, + ExtractedSupportEvent supportEvent, + List warnings + ) { + DriverExtractionBuilder builder = driversByKey.get(assignment.driverKey()); + if (builder == null) { + warnings.add(new ExtractionWarning( + "VU_SUPPORT_EVENT_DRIVER_MISSING", + "Support event matched a driver key without an initialized driver session.", + supportEvent.rawRecordPath() + )); + return; + } + builder.supportEvents.add(supportEvent); + } + + private List resolveDriverAssignments( + OffsetDateTime occurredAt, + String explicitDriverKey, + String explicitSlot, + List vuCardIwIntervals + ) { + if (explicitDriverKey != null) { + return List.of(new DriverAssignment(explicitDriverKey, explicitSlot)); + } + return vuCardIwIntervals.stream() + .filter(iw -> explicitSlot == null || explicitSlot.equals(iw.slot())) + .filter(iw -> iw.covers(occurredAt)) + .map(iw -> new DriverAssignment(iw.driverKey(), iw.slot())) + .toList(); + } + + private List distinctAssignments(List assignments) { + Map unique = new LinkedHashMap<>(); + for (DriverAssignment assignment : assignments) { + unique.putIfAbsent(assignment.driverKey() + "|" + assignment.slot(), assignment); + } + return List.copyOf(unique.values()); + } + + private String driverKeyFromCardNode(Element node, String basePath) { + String cardNation = text(node, basePath + "/cardIssuingMemberState"); + String cardNumber = joinCardNumber(node, basePath + "/cardNumber"); + return cardNumber == null ? null : driverKeyFactory.createDriverKey(cardNation, cardNumber); + } + private Element firstElement(Object node, String expression) { NodeList nodes = nodes(node, expression); if (nodes.getLength() == 0) { @@ -470,6 +720,26 @@ public class VehicleUnitXmlExtractionService { return Long.parseLong(value.trim()); } + private BigDecimal geoCoordinate(Object node, String expression, boolean latitude) { + String value = text(node, expression); + if (value == null) { + return null; + } + BigDecimal raw = new BigDecimal(value); + BigDecimal abs = raw.abs(); + BigDecimal degreeThreshold = BigDecimal.valueOf(latitude ? 90D : 180D); + if (abs.compareTo(degreeThreshold) <= 0) { + return raw; + } + BigDecimal sign = raw.signum() < 0 ? BigDecimal.valueOf(-1L) : BigDecimal.ONE; + BigDecimal absolute = raw.abs(); + BigDecimal[] degreeAndRemainder = absolute.divideAndRemainder(BigDecimal.valueOf(1000L)); + BigDecimal degrees = degreeAndRemainder[0]; + BigDecimal minutes = degreeAndRemainder[1].divide(BigDecimal.TEN); + BigDecimal decimalDegrees = degrees.add(minutes.divide(BigDecimal.valueOf(60L), 8, java.math.RoundingMode.HALF_UP)); + return decimalDegrees.multiply(sign); + } + private OffsetDateTime combine(LocalDate date, String timeText) { if (date == null || timeText == null || timeText.isBlank()) { return null; @@ -499,6 +769,24 @@ public class VehicleUnitXmlExtractionService { return value.trim().toUpperCase().replace('-', '_').replace(' ', '_'); } + private String mapPlaceEntryType(String entryType) { + String normalized = normalizeToken(entryType); + if (normalized == null) { + return "DAILY_WORK_PERIOD_PLACE"; + } + return switch (normalized) { + case "0" -> "BEGIN_DAILY_WORK_PERIOD"; + case "1" -> "END_DAILY_WORK_PERIOD"; + case "2" -> "BEGIN_MANUAL_DAILY_WORK_PERIOD"; + case "3" -> "END_MANUAL_DAILY_WORK_PERIOD"; + case "4" -> "BEGIN_ASSUMED_DAILY_WORK_PERIOD"; + case "5" -> "END_ASSUMED_DAILY_WORK_PERIOD"; + case "6" -> "BEGIN_GNSS_DAILY_WORK_PERIOD"; + case "7" -> "END_GNSS_DAILY_WORK_PERIOD"; + default -> "DAILY_WORK_PERIOD_PLACE"; + }; + } + private String joinCardNumber(Element node, String basePath) { String driverIdentification = text(node, basePath + "/driverIdentification"); if (driverIdentification == null) { @@ -525,6 +813,7 @@ public class VehicleUnitXmlExtractionService { private final Map vehiclesByKey = new LinkedHashMap<>(); private final List vehicleUsageIntervals = new ArrayList<>(); private final List cardActivityIntervals = new ArrayList<>(); + private final List supportEvents = new ArrayList<>(); private final List warnings = new ArrayList<>(); private DriverExtractionBuilder(String driverKey, String sourceDriverId) { @@ -588,6 +877,7 @@ public class VehicleUnitXmlExtractionService { List.copyOf(vehiclesByKey.values()), List.copyOf(vehicleUsageIntervals), List.copyOf(cardActivityIntervals), + List.copyOf(supportEvents), List.copyOf(warnings) ); } @@ -644,4 +934,10 @@ public class VehicleUnitXmlExtractionService { OffsetDateTime to ) { } + + private record DriverAssignment( + String driverKey, + String slot + ) { + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3f4d816..0e018ed 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -131,7 +131,7 @@ eventhub: password: ${LEGAL_REQUIREMENTS_PASSWORD:} connect-timeout: 20s read-timeout: 2m - reset-session-after-use: true + reset-session-after-use: false esper-poc: activity-merge-mode: JAVA diff --git a/src/test/java/at/procon/eventhub/config/EventHubPropertiesTest.java b/src/test/java/at/procon/eventhub/config/EventHubPropertiesTest.java new file mode 100644 index 0000000..43b2249 --- /dev/null +++ b/src/test/java/at/procon/eventhub/config/EventHubPropertiesTest.java @@ -0,0 +1,34 @@ +package at.procon.eventhub.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; + +class EventHubPropertiesTest { + + @Test + void legalRequirementsBaseUrlIsNormalizedWhenPrefixedWithColonAndWhitespace() { + EventHubProperties.LegalRequirements legalRequirements = new EventHubProperties.LegalRequirements(); + + legalRequirements.setBaseUrl(": https://legalrequirements.services.bytebar.eu/ODataV4/LR/"); + + assertEquals("https://legalrequirements.services.bytebar.eu/ODataV4/LR", legalRequirements.getBaseUrl()); + } + + @Test + void legalRequirementsBaseUrlFallsBackToDefaultWhenBlank() { + EventHubProperties.LegalRequirements legalRequirements = new EventHubProperties.LegalRequirements(); + + legalRequirements.setBaseUrl(" "); + + assertEquals("https://legalrequirements.services.bytebar.eu/ODataV4/LR", legalRequirements.getBaseUrl()); + } + + @Test + void legalRequirementsResetSessionIsDisabledByDefault() { + EventHubProperties.LegalRequirements legalRequirements = new EventHubProperties.LegalRequirements(); + + assertFalse(legalRequirements.isResetSessionAfterUse()); + } +} diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java index 176aee4..df5422e 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java @@ -52,7 +52,7 @@ class TachographFileSessionControllerTest { .thenReturn(new CreateTachographFileSessionResponse(summary)); when(service.getSession(sessionId)).thenReturn(summary); when(service.listDrivers(sessionId)).thenReturn(new TachographFileSessionListDriversResponse(sessionId, List.of(driver))); - when(service.getDriver(sessionId, "12:123")).thenReturn(new TachographFileDriverDetailDto(sessionId, "12:123", null, null, List.of(), List.of(), List.of(), List.of(), List.of())); + when(service.getDriver(sessionId, "12:123")).thenReturn(new TachographFileDriverDetailDto(sessionId, "12:123", null, null, List.of(), List.of(), List.of(), List.of(), List.of(), List.of())); when(service.deleteSession(sessionId)).thenReturn(new TachographFileSessionDeleteResponse(sessionId, true)); mockMvc.perform(multipart("/api/eventhub/tachograph-file-sessions") diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java index 7e57faf..0d43165 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlExtractionServiceTest.java @@ -56,12 +56,21 @@ class VehicleUnitXmlExtractionServiceTest { assertThat(firstDriver.cardActivityIntervals().get(0).activityType()).isEqualTo("WORK"); assertThat(firstDriver.cardActivityIntervals().get(1).activityType()).isEqualTo("DRIVE"); assertThat(firstDriver.cardActivityIntervals().get(2).to().toString()).isEqualTo("2026-04-01T11:00:01Z"); + assertThat(firstDriver.supportEvents()).hasSize(1); + assertThat(firstDriver.supportEvents().get(0).eventDomain()).isEqualTo("PLACE"); + assertThat(firstDriver.supportEvents().get(0).eventType()).isEqualTo("BEGIN_DAILY_WORK_PERIOD"); + assertThat(firstDriver.supportEvents().get(0).country()).isEqualTo("12"); + assertThat(firstDriver.supportEvents().get(0).latitude()).isNotNull(); assertThat(secondDriver.cardVehicleUsageIntervals()).hasSize(1); assertThat(secondDriver.cardVehicleUsageIntervals().get(0).to().toString()).isEqualTo("2026-04-02T10:00Z"); assertThat(secondDriver.cardActivityIntervals()).hasSize(2); assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z"); assertThat(secondDriver.cardActivityIntervals().get(1).to().toString()).isEqualTo("2026-04-02T10:00:01Z"); + assertThat(secondDriver.supportEvents()).hasSize(2); + assertThat(secondDriver.supportEvents()).extracting("eventDomain").containsExactly("POSITION", "SPECIFIC_CONDITION"); + assertThat(secondDriver.supportEvents().get(0).latitude()).isNotNull(); + assertThat(secondDriver.supportEvents().get(1).code()).isEqualTo("1"); assertThat(session.warnings()).extracting("code") .contains("OPEN_VU_CARD_INTERVAL", "VU_ACTIVITY_UNASSIGNED"); diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlSamples.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlSamples.java index cfe92b1..64b8018 100644 --- a/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlSamples.java +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/VehicleUnitXmlSamples.java @@ -108,6 +108,67 @@ final class VehicleUnitXmlSamples { 08:00:00Z + + + + DRIVER_CARD + 12 + + 123456789012 + 0 + 0 + + 2 + + + 2026-04-01T08:00:00Z + 0 + 12 + 9 + 1000 + + 2026-04-01T08:00:00Z + 5 + + 48.2082 + 16.3738 + + AUTHENTICATED + + + + + + + 2026-04-02T08:00:00Z + + DRIVER_CARD + 12 + + 999999999999 + 1 + 1 + + 2 + + + 2026-04-02T08:00:00Z + 4 + + 48012 + 16373 + + NOT_AUTHENTICATED + + 1210 + + + + + 2026-04-02T09:00:00Z + 1 + + """;