Extract VU support events into driver sessions
This commit is contained in:
parent
e30f98b2c0
commit
4ad6fd7dac
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<ExtractedVehicle> vehicles,
|
||||
List<ExtractedCardVehicleUsageInterval> cardVehicleUsageIntervals,
|
||||
List<ExtractedCardActivityInterval> cardActivityIntervals,
|
||||
List<ExtractedSupportEvent> supportEvents,
|
||||
List<ExtractionWarning> warnings
|
||||
) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ public record DriverExtractionSession(
|
|||
List<ExtractedVehicle> vehicles,
|
||||
List<ExtractedCardVehicleUsageInterval> cardVehicleUsageIntervals,
|
||||
List<ExtractedCardActivityInterval> cardActivityIntervals,
|
||||
List<ExtractedSupportEvent> supportEvents,
|
||||
List<ExtractionWarning> warnings
|
||||
) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
) {
|
||||
}
|
||||
|
|
@ -87,6 +87,7 @@ public class DriverCardXmlExtractionService {
|
|||
List.copyOf(vehiclesByKey.values()),
|
||||
List.copyOf(vehicleUsageIntervals),
|
||||
List.copyOf(activityIntervals),
|
||||
List.of(),
|
||||
List.copyOf(warnings)
|
||||
);
|
||||
Map<String, DriverExtractionSession> driversByKey = Map.of(driverKey, driverSession);
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ public class TachographFileSessionService {
|
|||
driver.vehicles(),
|
||||
driver.cardVehicleUsageIntervals(),
|
||||
driver.cardActivityIntervals(),
|
||||
driver.supportEvents(),
|
||||
driver.warnings()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ExtractedCardActivityInterval> vuActivityIntervals = extractActivityIntervals(document, vehicleContext, sessionWarnings);
|
||||
assignActivityCoverage(vuActivityIntervals, vuCardIwIntervals, vehicleContext, driversByKey, sessionWarnings);
|
||||
extractSupportEvents(document, vehicleContext, vuCardIwIntervals, driversByKey, sessionWarnings);
|
||||
|
||||
List<ExtractionWarning> allWarnings = new ArrayList<>(sessionWarnings);
|
||||
Map<String, DriverExtractionSession> driverSessions = new LinkedHashMap<>();
|
||||
|
|
@ -369,6 +372,205 @@ public class VehicleUnitXmlExtractionService {
|
|||
}
|
||||
}
|
||||
|
||||
private void extractSupportEvents(
|
||||
Document document,
|
||||
VehicleContext vehicleContext,
|
||||
List<VuCardIwInterval> vuCardIwIntervals,
|
||||
Map<String, DriverExtractionBuilder> driversByKey,
|
||||
List<ExtractionWarning> 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<VuCardIwInterval> vuCardIwIntervals,
|
||||
Map<String, DriverExtractionBuilder> driversByKey,
|
||||
List<ExtractionWarning> 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<DriverAssignment> 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<VuCardIwInterval> vuCardIwIntervals,
|
||||
Map<String, DriverExtractionBuilder> driversByKey,
|
||||
List<ExtractionWarning> 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<DriverAssignment> 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<VuCardIwInterval> vuCardIwIntervals,
|
||||
Map<String, DriverExtractionBuilder> driversByKey,
|
||||
List<ExtractionWarning> 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<DriverAssignment> 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<ActivitySegment> segments) {
|
||||
if (segments.isEmpty()) {
|
||||
return true;
|
||||
|
|
@ -429,6 +631,54 @@ public class VehicleUnitXmlExtractionService {
|
|||
return segments;
|
||||
}
|
||||
|
||||
private void addSupportEvent(
|
||||
Map<String, DriverExtractionBuilder> driversByKey,
|
||||
DriverAssignment assignment,
|
||||
ExtractedSupportEvent supportEvent,
|
||||
List<ExtractionWarning> 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<DriverAssignment> resolveDriverAssignments(
|
||||
OffsetDateTime occurredAt,
|
||||
String explicitDriverKey,
|
||||
String explicitSlot,
|
||||
List<VuCardIwInterval> 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<DriverAssignment> distinctAssignments(List<DriverAssignment> assignments) {
|
||||
Map<String, DriverAssignment> 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<String, ExtractedVehicle> vehiclesByKey = new LinkedHashMap<>();
|
||||
private final List<ExtractedCardVehicleUsageInterval> vehicleUsageIntervals = new ArrayList<>();
|
||||
private final List<ExtractedCardActivityInterval> cardActivityIntervals = new ArrayList<>();
|
||||
private final List<ExtractedSupportEvent> supportEvents = new ArrayList<>();
|
||||
private final List<ExtractionWarning> 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
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -108,6 +108,67 @@ final class VehicleUnitXmlSamples {
|
|||
<timeOfChange>08:00:00Z</timeOfChange>
|
||||
</activityChangeInfos>
|
||||
</vuActivityDailyData>
|
||||
<vuPlaceDailyWorkPeriodData>
|
||||
<vuPlaceDailyWorkPeriodRecords>
|
||||
<fullCardNumber>
|
||||
<cardType>DRIVER_CARD</cardType>
|
||||
<cardIssuingMemberState>12</cardIssuingMemberState>
|
||||
<cardNumber>
|
||||
<driverIdentification>123456789012</driverIdentification>
|
||||
<cardReplacementIndex>0</cardReplacementIndex>
|
||||
<cardRenewalIndex>0</cardRenewalIndex>
|
||||
</cardNumber>
|
||||
<generation>2</generation>
|
||||
</fullCardNumber>
|
||||
<placeRecord>
|
||||
<entryTime>2026-04-01T08:00:00Z</entryTime>
|
||||
<entryTypeDailyWorkPeriod>0</entryTypeDailyWorkPeriod>
|
||||
<dailyWorkPeriodCountry>12</dailyWorkPeriodCountry>
|
||||
<dailyWorkPeriodRegion>9</dailyWorkPeriodRegion>
|
||||
<vehicleOdometerValue>1000</vehicleOdometerValue>
|
||||
<entryGnssPlaceRecord>
|
||||
<timeStamp>2026-04-01T08:00:00Z</timeStamp>
|
||||
<gnssAccuracy>5</gnssAccuracy>
|
||||
<geoCoordinates>
|
||||
<latitude>48.2082</latitude>
|
||||
<longitude>16.3738</longitude>
|
||||
</geoCoordinates>
|
||||
<authenticationStatus>AUTHENTICATED</authenticationStatus>
|
||||
</entryGnssPlaceRecord>
|
||||
</placeRecord>
|
||||
</vuPlaceDailyWorkPeriodRecords>
|
||||
</vuPlaceDailyWorkPeriodData>
|
||||
<vuGnssADData>
|
||||
<vuGnssADRecord>
|
||||
<timeStamp>2026-04-02T08:00:00Z</timeStamp>
|
||||
<cardNumberDriverSlot>
|
||||
<cardType>DRIVER_CARD</cardType>
|
||||
<cardIssuingMemberState>12</cardIssuingMemberState>
|
||||
<cardNumber>
|
||||
<driverIdentification>999999999999</driverIdentification>
|
||||
<cardReplacementIndex>1</cardReplacementIndex>
|
||||
<cardRenewalIndex>1</cardRenewalIndex>
|
||||
</cardNumber>
|
||||
<generation>2</generation>
|
||||
</cardNumberDriverSlot>
|
||||
<gnssPlaceRecord>
|
||||
<timeStamp>2026-04-02T08:00:00Z</timeStamp>
|
||||
<gnssAccuracy>4</gnssAccuracy>
|
||||
<geoCoordinates>
|
||||
<latitude>48012</latitude>
|
||||
<longitude>16373</longitude>
|
||||
</geoCoordinates>
|
||||
<authenticationStatus>NOT_AUTHENTICATED</authenticationStatus>
|
||||
</gnssPlaceRecord>
|
||||
<vehicleOdometerValue>1210</vehicleOdometerValue>
|
||||
</vuGnssADRecord>
|
||||
</vuGnssADData>
|
||||
<vuSpecificConditionData>
|
||||
<specificConditionRecords>
|
||||
<entryTime>2026-04-02T09:00:00Z</entryTime>
|
||||
<specificConditionType>1</specificConditionType>
|
||||
</specificConditionRecords>
|
||||
</vuSpecificConditionData>
|
||||
</Activities>
|
||||
</VehicleUnit>
|
||||
""";
|
||||
|
|
|
|||
Loading…
Reference in New Issue