diff --git a/src/main/java/at/procon/eventhub/config/EventHubProperties.java b/src/main/java/at/procon/eventhub/config/EventHubProperties.java index 85092b0..2987477 100644 --- a/src/main/java/at/procon/eventhub/config/EventHubProperties.java +++ b/src/main/java/at/procon/eventhub/config/EventHubProperties.java @@ -30,6 +30,7 @@ public class EventHubProperties { private final Batch batch = new Batch(); private final Tachograph tachograph = new Tachograph(); + private final TachographFileSession tachographFileSession = new TachographFileSession(); private final EsperPoc esperPoc = new EsperPoc(); private final YellowFox yellowFox = new YellowFox(); @@ -41,6 +42,10 @@ public class EventHubProperties { return tachograph; } + public TachographFileSession getTachographFileSession() { + return tachographFileSession; + } + public EsperPoc getEsperPoc() { return esperPoc; } @@ -308,6 +313,104 @@ public class EventHubProperties { } } + public static class TachographFileSession { + private Duration ttl = Duration.ofHours(4); + private int maxSessions = 100; + private long maxFileSizeBytes = 20L * 1024L * 1024L; + private final LegalRequirements legalRequirements = new LegalRequirements(); + + public Duration getTtl() { + return ttl; + } + + public void setTtl(Duration ttl) { + if (ttl != null && !ttl.isNegative() && !ttl.isZero()) { + this.ttl = ttl; + } + } + + public int getMaxSessions() { + return maxSessions; + } + + public void setMaxSessions(int maxSessions) { + this.maxSessions = Math.max(1, maxSessions); + } + + public long getMaxFileSizeBytes() { + return maxFileSizeBytes; + } + + public void setMaxFileSizeBytes(long maxFileSizeBytes) { + this.maxFileSizeBytes = Math.max(1024L, maxFileSizeBytes); + } + + public LegalRequirements getLegalRequirements() { + return legalRequirements; + } + } + + public static class LegalRequirements { + private String baseUrl = "https://legalrequirements.services.bytebar.eu/ODataV4/LR"; + private String username; + private String password; + private Duration connectTimeout = Duration.ofSeconds(20); + private Duration readTimeout = Duration.ofMinutes(2); + private boolean resetSessionAfterUse = true; + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Duration getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(Duration connectTimeout) { + if (connectTimeout != null && !connectTimeout.isNegative() && !connectTimeout.isZero()) { + this.connectTimeout = connectTimeout; + } + } + + public Duration getReadTimeout() { + return readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + if (readTimeout != null && !readTimeout.isNegative() && !readTimeout.isZero()) { + this.readTimeout = readTimeout; + } + } + + public boolean isResetSessionAfterUse() { + return resetSessionAfterUse; + } + + public void setResetSessionAfterUse(boolean resetSessionAfterUse) { + this.resetSessionAfterUse = resetSessionAfterUse; + } + } + public static class TachographDataSource { private String jdbcUrl; private String username; diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionController.java b/src/main/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionController.java new file mode 100644 index 0000000..ea10ab8 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionController.java @@ -0,0 +1,64 @@ +package at.procon.eventhub.tachographfilesession.api; + +import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto; +import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService; +import java.util.UUID; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +@RequestMapping("/api/eventhub/tachograph-file-sessions") +public class TachographFileSessionController { + + private final TachographFileSessionService service; + + public TachographFileSessionController(TachographFileSessionService service) { + this.service = service; + } + + @PostMapping + public ResponseEntity createSession( + @RequestParam("file") MultipartFile file, + @RequestParam(required = false) String tenantKey, + @RequestParam(required = false) String sourceInstanceKey, + @RequestParam(required = false) String sessionLabel + ) { + return ResponseEntity.status(HttpStatus.CREATED) + .body(service.createSession(file, tenantKey, sourceInstanceKey, sessionLabel)); + } + + @GetMapping("/{sessionId}") + public ResponseEntity getSession(@PathVariable UUID sessionId) { + return ResponseEntity.ok(service.getSession(sessionId)); + } + + @GetMapping("/{sessionId}/drivers") + public ResponseEntity listDrivers(@PathVariable UUID sessionId) { + return ResponseEntity.ok(service.listDrivers(sessionId)); + } + + @GetMapping("/{sessionId}/drivers/{driverKey}") + public ResponseEntity getDriver( + @PathVariable UUID sessionId, + @PathVariable String driverKey + ) { + return ResponseEntity.ok(service.getDriver(sessionId, driverKey)); + } + + @DeleteMapping("/{sessionId}") + public ResponseEntity deleteSession(@PathVariable UUID sessionId) { + return ResponseEntity.ok(service.deleteSession(sessionId)); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionExceptionHandler.java b/src/main/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionExceptionHandler.java new file mode 100644 index 0000000..34e6962 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionExceptionHandler.java @@ -0,0 +1,52 @@ +package at.procon.eventhub.tachographfilesession.api; + +import at.procon.eventhub.tachographfilesession.service.DriverNotFoundInSessionException; +import at.procon.eventhub.tachographfilesession.service.LegalRequirementsUploadException; +import at.procon.eventhub.tachographfilesession.service.LegalRequirementsXmlDownloadException; +import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException; +import at.procon.eventhub.tachographfilesession.service.TachographXmlValidationException; +import at.procon.eventhub.tachographfilesession.service.UnsupportedTachographFileTypeException; +import java.time.OffsetDateTime; +import java.util.Map; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice(basePackageClasses = TachographFileSessionController.class) +public class TachographFileSessionExceptionHandler { + + @ExceptionHandler({ + TachographFileSessionNotFoundException.class, + DriverNotFoundInSessionException.class + }) + public ResponseEntity> notFound(RuntimeException exception) { + return error(HttpStatus.NOT_FOUND, exception); + } + + @ExceptionHandler({ + IllegalArgumentException.class, + TachographXmlValidationException.class, + UnsupportedTachographFileTypeException.class + }) + public ResponseEntity> badRequest(RuntimeException exception) { + return error(HttpStatus.BAD_REQUEST, exception); + } + + @ExceptionHandler({ + LegalRequirementsUploadException.class, + LegalRequirementsXmlDownloadException.class + }) + public ResponseEntity> badGateway(RuntimeException exception) { + return error(HttpStatus.BAD_GATEWAY, exception); + } + + private ResponseEntity> error(HttpStatus status, RuntimeException exception) { + return ResponseEntity.status(status).body(Map.of( + "timestamp", OffsetDateTime.now().toString(), + "status", status.value(), + "error", status.getReasonPhrase(), + "message", exception.getMessage() + )); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/CreateTachographFileSessionResponse.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/CreateTachographFileSessionResponse.java new file mode 100644 index 0000000..aea1251 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/CreateTachographFileSessionResponse.java @@ -0,0 +1,6 @@ +package at.procon.eventhub.tachographfilesession.dto; + +public record CreateTachographFileSessionResponse( + TachographFileSessionSummaryDto session +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java new file mode 100644 index 0000000..462ab1d --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverDetailDto.java @@ -0,0 +1,24 @@ +package at.procon.eventhub.tachographfilesession.dto; + +import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval; +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.ExtractedVehicle; +import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration; +import at.procon.eventhub.tachographfilesession.model.ExtractionWarning; +import java.util.List; +import java.util.UUID; + +public record TachographFileDriverDetailDto( + UUID sessionId, + String driverKey, + ExtractedDriver driver, + ExtractedDriverCard driverCard, + List vehicleRegistrations, + List vehicles, + List cardVehicleUsageIntervals, + List cardActivityIntervals, + List warnings +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverSummaryDto.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverSummaryDto.java new file mode 100644 index 0000000..84ea60e --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileDriverSummaryDto.java @@ -0,0 +1,12 @@ +package at.procon.eventhub.tachographfilesession.dto; + +public record TachographFileDriverSummaryDto( + String driverKey, + String surname, + String firstNames, + String cardNation, + String cardNumber, + int activityIntervalCount, + int cardVehicleUsageIntervalCount +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionDeleteResponse.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionDeleteResponse.java new file mode 100644 index 0000000..3cf979f --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionDeleteResponse.java @@ -0,0 +1,9 @@ +package at.procon.eventhub.tachographfilesession.dto; + +import java.util.UUID; + +public record TachographFileSessionDeleteResponse( + UUID sessionId, + boolean deleted +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionListDriversResponse.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionListDriversResponse.java new file mode 100644 index 0000000..9ac6b52 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionListDriversResponse.java @@ -0,0 +1,10 @@ +package at.procon.eventhub.tachographfilesession.dto; + +import java.util.List; +import java.util.UUID; + +public record TachographFileSessionListDriversResponse( + UUID sessionId, + List drivers +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionSummaryDto.java b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionSummaryDto.java new file mode 100644 index 0000000..7e84ede --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/dto/TachographFileSessionSummaryDto.java @@ -0,0 +1,23 @@ +package at.procon.eventhub.tachographfilesession.dto; + +import at.procon.eventhub.tachographfilesession.model.ExtractionStats; +import at.procon.eventhub.tachographfilesession.model.ExtractionWarning; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +public record TachographFileSessionSummaryDto( + UUID sessionId, + String tenantKey, + String sourceInstanceKey, + String sessionLabel, + String originalFileName, + boolean driverCardFile, + String legalRequirementsDataPackageId, + ExtractionStats stats, + List drivers, + List warnings, + Instant createdAt, + Instant expiresAt +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java new file mode 100644 index 0000000..93aff39 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/DriverExtractionSession.java @@ -0,0 +1,15 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.util.List; + +public record DriverExtractionSession( + String driverKey, + ExtractedDriver driver, + ExtractedDriverCard driverCard, + List vehicleRegistrations, + List vehicles, + List cardVehicleUsageIntervals, + List cardActivityIntervals, + List warnings +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedCardActivityInterval.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedCardActivityInterval.java new file mode 100644 index 0000000..01b2e93 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedCardActivityInterval.java @@ -0,0 +1,17 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.time.OffsetDateTime; + +public record ExtractedCardActivityInterval( + String intervalId, + OffsetDateTime from, + OffsetDateTime to, + String activityType, + String slot, + String cardStatus, + String drivingStatus, + String registrationKey, + String vehicleKey, + String rawRecordPath +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedCardVehicleUsageInterval.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedCardVehicleUsageInterval.java new file mode 100644 index 0000000..39a58ec --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedCardVehicleUsageInterval.java @@ -0,0 +1,15 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.time.OffsetDateTime; + +public record ExtractedCardVehicleUsageInterval( + String intervalId, + OffsetDateTime from, + OffsetDateTime to, + Long odometerBeginKm, + Long odometerEndKm, + String registrationKey, + String vehicleKey, + String rawRecordPath +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriver.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriver.java new file mode 100644 index 0000000..fb4d2c4 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriver.java @@ -0,0 +1,16 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.time.LocalDate; + +public record ExtractedDriver( + String driverKey, + String sourceDriverId, + String surname, + String firstNames, + LocalDate birthDate, + String preferredLanguage, + String drivingLicenceNumber, + String drivingLicenceNation, + String drivingLicenceAuthority +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java new file mode 100644 index 0000000..3e950b6 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedDriverCard.java @@ -0,0 +1,14 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.time.OffsetDateTime; + +public record ExtractedDriverCard( + String sourceDriverCardId, + String cardNation, + String cardNumber, + String issuingAuthorityName, + OffsetDateTime issueDate, + OffsetDateTime validityBegin, + OffsetDateTime expiryDate +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedVehicle.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedVehicle.java new file mode 100644 index 0000000..a0aba8b --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedVehicle.java @@ -0,0 +1,8 @@ +package at.procon.eventhub.tachographfilesession.model; + +public record ExtractedVehicle( + String vehicleKey, + String sourceVehicleId, + String vin +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedVehicleRegistration.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedVehicleRegistration.java new file mode 100644 index 0000000..1fb5ed9 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractedVehicleRegistration.java @@ -0,0 +1,9 @@ +package at.procon.eventhub.tachographfilesession.model; + +public record ExtractedVehicleRegistration( + String registrationKey, + String sourceVehicleRegistrationId, + String registrationNation, + String registrationNumber +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractionStats.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractionStats.java new file mode 100644 index 0000000..203c103 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractionStats.java @@ -0,0 +1,11 @@ +package at.procon.eventhub.tachographfilesession.model; + +public record ExtractionStats( + int driverCount, + int activityIntervalCount, + int cardVehicleUsageIntervalCount, + int vehicleRegistrationCount, + int vehicleCount, + int warningCount +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractionWarning.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractionWarning.java new file mode 100644 index 0000000..d9e0562 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/ExtractionWarning.java @@ -0,0 +1,8 @@ +package at.procon.eventhub.tachographfilesession.model; + +public record ExtractionWarning( + String code, + String message, + String rawRecordPath +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/TachographFileSession.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/TachographFileSession.java new file mode 100644 index 0000000..e6a1c69 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/TachographFileSession.java @@ -0,0 +1,16 @@ +package at.procon.eventhub.tachographfilesession.model; + +import java.time.Instant; +import java.util.Map; +import java.util.UUID; + +public record TachographFileSession( + UUID sessionId, + TachographFileSessionMetadata metadata, + Map driversByKey, + ExtractionStats extractionStats, + java.util.List warnings, + Instant createdAt, + Instant expiresAt +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/model/TachographFileSessionMetadata.java b/src/main/java/at/procon/eventhub/tachographfilesession/model/TachographFileSessionMetadata.java new file mode 100644 index 0000000..82b4f51 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/model/TachographFileSessionMetadata.java @@ -0,0 +1,15 @@ +package at.procon.eventhub.tachographfilesession.model; + +public record TachographFileSessionMetadata( + String tenantKey, + String sourceInstanceKey, + String sessionLabel, + String originalFileName, + String uploadedFileSha256, + long uploadedFileSize, + String legalRequirementsDataPackageId, + String xmlSha256, + boolean driverCardFile, + String xmlGeneration +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java new file mode 100644 index 0000000..768e9c0 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionService.java @@ -0,0 +1,408 @@ +package at.procon.eventhub.tachographfilesession.service; + +import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession; +import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval; +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.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.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import org.springframework.stereotype.Component; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +@Component +public class DriverCardXmlExtractionService { + + private final DriverKeyFactory driverKeyFactory; + private final VehicleKeyFactory vehicleKeyFactory; + + public DriverCardXmlExtractionService(DriverKeyFactory driverKeyFactory, VehicleKeyFactory vehicleKeyFactory) { + this.driverKeyFactory = driverKeyFactory; + this.vehicleKeyFactory = vehicleKeyFactory; + } + + public TachographFileSession extract( + TachographXmlParser.ParsedTachographXml parsedXml, + TachographFileSessionMetadata metadata, + Instant createdAt, + Instant expiresAt + ) { + Document document = parsedXml.document(); + List warnings = new ArrayList<>(); + + ExtractedDriverCard driverCard = extractDriverCard(document, warnings); + if (driverCard == null || driverCard.cardNumber() == null) { + throw new TachographXmlValidationException("Driver card identification could not be extracted from XML."); + } + String driverKey = driverKeyFactory.createDriverKey(driverCard.cardNation(), driverCard.cardNumber()); + driverCard = new ExtractedDriverCard( + driverKeyFactory.createSourceDriverCardId(driverKey), + driverCard.cardNation(), + driverCard.cardNumber(), + driverCard.issuingAuthorityName(), + driverCard.issueDate(), + driverCard.validityBegin(), + driverCard.expiryDate() + ); + ExtractedDriver driver = extractDriver(document, driverKey, warnings); + + LinkedHashMap registrationsByKey = new LinkedHashMap<>(); + LinkedHashMap vehiclesByKey = new LinkedHashMap<>(); + List vehicleUsageIntervals = + extractVehicleUsageIntervals(document, registrationsByKey, vehiclesByKey, warnings); + List activityIntervals = + assignVehicleCoverage(extractActivityIntervals(document, warnings), vehicleUsageIntervals); + + DriverExtractionSession driverSession = new DriverExtractionSession( + driverKey, + driver, + driverCard, + List.copyOf(registrationsByKey.values()), + List.copyOf(vehiclesByKey.values()), + List.copyOf(vehicleUsageIntervals), + List.copyOf(activityIntervals), + List.copyOf(warnings) + ); + Map driversByKey = Map.of(driverKey, driverSession); + ExtractionStats stats = new ExtractionStats( + 1, + activityIntervals.size(), + vehicleUsageIntervals.size(), + registrationsByKey.size(), + vehiclesByKey.size(), + warnings.size() + ); + return new TachographFileSession( + UUID.randomUUID(), + metadata, + driversByKey, + stats, + List.copyOf(warnings), + createdAt, + expiresAt + ); + } + + private ExtractedDriverCard extractDriverCard(Document document, List warnings) { + Element identification = firstElement(document, "/DriverCard/Identification[1]"); + if (identification == null) { + warnings.add(new ExtractionWarning("MISSING_IDENTIFICATION", "Driver card identification block is missing.", "/DriverCard")); + return null; + } + String cardNation = text(identification, "cardIdentification/cardIssuingMemberState"); + String cardNumber = joinCardNumber(identification); + String authority = text(identification, "cardIdentification/cardIssuingAuthorityName/name"); + return new ExtractedDriverCard( + null, + cardNation, + cardNumber, + authority, + offsetDateTime(text(identification, "cardIdentification/cardIssueDate")), + offsetDateTime(text(identification, "cardIdentification/cardValidityBegin")), + offsetDateTime(text(identification, "cardIdentification/cardExpiryDate")) + ); + } + + private ExtractedDriver extractDriver(Document document, String driverKey, List warnings) { + Element identification = firstElement(document, "/DriverCard/Identification[1]"); + if (identification == null) { + warnings.add(new ExtractionWarning("MISSING_DRIVER", "Driver holder identification block is missing.", "/DriverCard")); + return new ExtractedDriver( + driverKey, + driverKeyFactory.createSourceDriverId(driverKey), + null, + null, + null, + null, + null, + null, + null + ); + } + Element licenceInfo = firstElement(document, "/DriverCard/DrivingLicenseInfo[1]"); + return new ExtractedDriver( + driverKey, + driverKeyFactory.createSourceDriverId(driverKey), + text(identification, "driverCardHolderIdentification/cardHolderName/holderSurname/name"), + text(identification, "driverCardHolderIdentification/cardHolderName/holderFirstNames/name"), + localDate(identification, "driverCardHolderIdentification/cardHolderBirthDate"), + text(identification, "driverCardHolderIdentification/cardHolderPreferredLanguage"), + text(licenceInfo, "cardDrivingLicenseInformation/drivingLicenceNumber"), + text(licenceInfo, "cardDrivingLicenseInformation/drivingLicenceIssuingNation"), + text(licenceInfo, "cardDrivingLicenseInformation/drivingLicenceIssuingAuthority/name") + ); + } + + private List extractVehicleUsageIntervals( + Document document, + Map registrationsByKey, + Map vehiclesByKey, + List warnings + ) { + NodeList records = nodes(document, "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords"); + List intervals = new ArrayList<>(); + for (int i = 0; i < records.getLength(); i++) { + Element record = (Element) records.item(i); + String path = "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords[" + (i + 1) + "]"; + OffsetDateTime from = offsetDateTime(text(record, "vehicleFirstUse")); + OffsetDateTime to = offsetDateTime(text(record, "vehicleLastUse")); + String registrationNation = text(record, "vehicleRegistration/vehicleRegistrationNation"); + String registrationNumber = text(record, "vehicleRegistration/vehicleRegistrationNumber/vehicleRegNumber"); + String registrationKey = vehicleKeyFactory.createRegistrationKey(registrationNation, registrationNumber); + registrationsByKey.putIfAbsent( + registrationKey, + new ExtractedVehicleRegistration( + registrationKey, + vehicleKeyFactory.createSourceVehicleRegistrationId(registrationKey), + registrationNation, + registrationNumber + ) + ); + String vin = text(record, "vehicleIdentificationNumber"); + String vehicleKey = vehicleKeyFactory.createVehicleKey(vin); + if (vehicleKey != null) { + vehiclesByKey.putIfAbsent( + vehicleKey, + new ExtractedVehicle(vehicleKey, vehicleKeyFactory.createSourceVehicleId(vehicleKey), vin) + ); + } + if (from == null || to == null) { + warnings.add(new ExtractionWarning("INCOMPLETE_VEHICLE_USAGE", "Vehicle usage interval is missing start or end timestamp.", path)); + continue; + } + intervals.add(new ExtractedCardVehicleUsageInterval( + "CVU-" + (i + 1), + from, + to, + longValue(text(record, "vehicleOdometerBegin")), + longValue(text(record, "vehicleOdometerEnd")), + registrationKey, + vehicleKey, + path + )); + } + intervals.sort(Comparator.comparing(ExtractedCardVehicleUsageInterval::from)); + return intervals; + } + + private List extractActivityIntervals(Document document, List warnings) { + NodeList dayRecords = nodes(document, "/DriverCard/DriverActivityData/cardDriverActivity/cardActivityDailyRecord"); + List intervals = new ArrayList<>(); + int intervalNo = 0; + for (int dayIndex = 0; dayIndex < dayRecords.getLength(); dayIndex++) { + Element dayRecord = (Element) dayRecords.item(dayIndex); + String dayPath = "/DriverCard/DriverActivityData/cardDriverActivity/cardActivityDailyRecord[" + (dayIndex + 1) + "]"; + OffsetDateTime recordDate = offsetDateTime(text(dayRecord, "activityRecordDate")); + if (recordDate == null) { + warnings.add(new ExtractionWarning("MISSING_ACTIVITY_RECORD_DATE", "Activity daily record has no activityRecordDate.", dayPath)); + continue; + } + LocalDate date = recordDate.withOffsetSameInstant(ZoneOffset.UTC).toLocalDate(); + NodeList changes = nodes(dayRecord, "activityChangeInfos"); + List parsedChanges = new ArrayList<>(); + for (int changeIndex = 0; changeIndex < changes.getLength(); changeIndex++) { + Element change = (Element) changes.item(changeIndex); + OffsetDateTime from = combine(date, text(change, "timeOfChange")); + if (from == null) { + warnings.add(new ExtractionWarning("INVALID_ACTIVITY_CHANGE_TIME", "Activity change has invalid timeOfChange.", dayPath + "/activityChangeInfos[" + (changeIndex + 1) + "]")); + continue; + } + parsedChanges.add(new ActivityChange( + from, + normalizeActivity(text(change, "activity")), + normalizeToken(text(change, "slot")), + normalizeToken(text(change, "cardStatus")), + normalizeToken(text(change, "drivingStatus")), + dayPath + "/activityChangeInfos[" + (changeIndex + 1) + "]" + )); + } + parsedChanges.sort(Comparator.comparing(ActivityChange::from)); + for (int i = 0; i < parsedChanges.size(); i++) { + ActivityChange current = parsedChanges.get(i); + OffsetDateTime to = i + 1 < parsedChanges.size() + ? parsedChanges.get(i + 1).from() + : date.plusDays(1).atStartOfDay().atOffset(ZoneOffset.UTC); + if (!current.from().isBefore(to)) { + continue; + } + intervalNo++; + intervals.add(new ExtractedCardActivityInterval( + "ACT-" + intervalNo, + current.from(), + to, + current.activityType(), + current.slot(), + current.cardStatus(), + current.drivingStatus(), + null, + null, + current.rawRecordPath() + )); + } + } + intervals.sort(Comparator.comparing(ExtractedCardActivityInterval::from)); + return intervals; + } + + private List assignVehicleCoverage( + List activityIntervals, + List vehicleUsageIntervals + ) { + List result = new ArrayList<>(activityIntervals.size()); + for (ExtractedCardActivityInterval interval : activityIntervals) { + ExtractedCardVehicleUsageInterval covering = vehicleUsageIntervals.stream() + .filter(usage -> !usage.from().isAfter(interval.from()) && !usage.to().isBefore(interval.from())) + .findFirst() + .orElse(null); + result.add(new ExtractedCardActivityInterval( + interval.intervalId(), + interval.from(), + interval.to(), + interval.activityType(), + interval.slot(), + interval.cardStatus(), + interval.drivingStatus(), + covering == null ? null : covering.registrationKey(), + covering == null ? null : covering.vehicleKey(), + interval.rawRecordPath() + )); + } + return result; + } + + private Element firstElement(Object node, String expression) { + NodeList nodes = nodes(node, expression); + if (nodes.getLength() == 0) { + return null; + } + return (Element) nodes.item(0); + } + + private NodeList nodes(Object node, String expression) { + try { + XPath xpath = XPathFactory.newInstance().newXPath(); + return (NodeList) xpath.evaluate(expression, node, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new IllegalStateException("Invalid XPath expression: " + expression, e); + } + } + + private String text(Object node, String expression) { + try { + XPath xpath = XPathFactory.newInstance().newXPath(); + String value = xpath.evaluate(expression, node); + return value == null || value.isBlank() ? null : value.trim(); + } catch (XPathExpressionException e) { + throw new IllegalStateException("Invalid XPath expression: " + expression, e); + } + } + + private OffsetDateTime offsetDateTime(String value) { + if (value == null || value.isBlank()) { + return null; + } + return OffsetDateTime.parse(value.trim()).withOffsetSameInstant(ZoneOffset.UTC); + } + + private LocalDate localDate(Element element, String expression) { + Element dateElement = firstElement(element, expression); + if (dateElement == null) { + return null; + } + String year = text(dateElement, "year"); + String month = text(dateElement, "month"); + String day = text(dateElement, "day"); + if (year == null || month == null || day == null) { + return null; + } + return LocalDate.of(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day)); + } + + private OffsetDateTime combine(LocalDate date, String timeText) { + if (date == null || timeText == null || timeText.isBlank()) { + return null; + } + LocalTime time = java.time.OffsetTime.parse(timeText.trim()).withOffsetSameInstant(ZoneOffset.UTC).toLocalTime(); + return date.atTime(time).atOffset(ZoneOffset.UTC); + } + + private Long longValue(String value) { + if (value == null || value.isBlank()) { + return null; + } + return Long.parseLong(value.trim()); + } + + private String normalizeActivity(String value) { + String normalized = normalizeToken(value); + if (normalized == null) { + return "UNKNOWN_ACTIVITY"; + } + return switch (normalized) { + case "DRIVING", "DRIVE" -> "DRIVE"; + case "WORK" -> "WORK"; + case "AVAILABILITY", "AVAILABLE" -> "AVAILABILITY"; + case "BREAK_REST", "BREAK/REST", "REST" -> "BREAK_REST"; + default -> "UNKNOWN_ACTIVITY"; + }; + } + + private String normalizeToken(String value) { + if (value == null || value.isBlank()) { + return null; + } + return value.trim().toUpperCase().replace('-', '_').replace(' ', '_'); + } + + private record ActivityChange( + OffsetDateTime from, + String activityType, + String slot, + String cardStatus, + String drivingStatus, + String rawRecordPath + ) { + } + + private String joinCardNumber(Element identification) { + String driverIdentification = text(identification, "cardIdentification/cardNumber/driverIdentification"); + if (driverIdentification == null) { + return null; + } + String replacement = text(identification, "cardIdentification/cardNumber/cardReplacementIndex"); + String renewal = text(identification, "cardIdentification/cardNumber/cardRenewalIndex"); + StringBuilder builder = new StringBuilder(driverIdentification); + if (replacement != null) { + builder.append(replacement); + } + if (renewal != null) { + builder.append(renewal); + } + return builder.toString(); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java new file mode 100644 index 0000000..2b8ca0a --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverKeyFactory.java @@ -0,0 +1,28 @@ +package at.procon.eventhub.tachographfilesession.service; + +import org.springframework.stereotype.Component; + +@Component +public class DriverKeyFactory { + + public String createDriverKey(String cardNation, String cardNumber) { + String normalizedNation = normalize(cardNation, "UNKNOWN"); + String normalizedCardNumber = normalize(cardNumber, "UNKNOWN"); + return normalizedNation + ":" + normalizedCardNumber; + } + + public String createSourceDriverId(String driverKey) { + return "DRV:" + driverKey; + } + + public String createSourceDriverCardId(String driverKey) { + return "CARD:" + driverKey; + } + + private String normalize(String value, String fallback) { + if (value == null || value.isBlank()) { + return fallback; + } + return value.trim(); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverNotFoundInSessionException.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverNotFoundInSessionException.java new file mode 100644 index 0000000..626681d --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/DriverNotFoundInSessionException.java @@ -0,0 +1,10 @@ +package at.procon.eventhub.tachographfilesession.service; + +import java.util.UUID; + +public class DriverNotFoundInSessionException extends RuntimeException { + + public DriverNotFoundInSessionException(UUID sessionId, String driverKey) { + super("Driver not found in session " + sessionId + ": " + driverKey); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/InMemoryTachographFileSessionRepository.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/InMemoryTachographFileSessionRepository.java new file mode 100644 index 0000000..a3f441a --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/InMemoryTachographFileSessionRepository.java @@ -0,0 +1,55 @@ +package at.procon.eventhub.tachographfilesession.service; + +import at.procon.eventhub.config.EventHubProperties; +import at.procon.eventhub.tachographfilesession.model.TachographFileSession; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.stereotype.Repository; + +@Repository +public class InMemoryTachographFileSessionRepository implements TachographFileSessionRepository { + + private final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); + private final EventHubProperties properties; + + public InMemoryTachographFileSessionRepository(EventHubProperties properties) { + this.properties = properties; + } + + @Override + public TachographFileSession save(TachographFileSession session) { + cleanupExpired(); + if (!sessions.containsKey(session.sessionId()) + && sessions.size() >= properties.getTachographFileSession().getMaxSessions()) { + throw new IllegalStateException("Maximum number of tachograph file sessions reached."); + } + sessions.put(session.sessionId(), session); + return session; + } + + @Override + public Optional find(UUID sessionId) { + cleanupExpired(); + return Optional.ofNullable(sessions.get(sessionId)); + } + + @Override + public boolean delete(UUID sessionId) { + cleanupExpired(); + return sessions.remove(sessionId) != null; + } + + @Override + public List list() { + cleanupExpired(); + return List.copyOf(sessions.values()); + } + + private void cleanupExpired() { + Instant now = Instant.now(); + sessions.entrySet().removeIf(entry -> entry.getValue().expiresAt() != null && entry.getValue().expiresAt().isBefore(now)); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsClient.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsClient.java new file mode 100644 index 0000000..6612aab --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsClient.java @@ -0,0 +1,126 @@ +package at.procon.eventhub.tachographfilesession.service; + +import at.procon.eventhub.config.EventHubProperties; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Base64; +import org.springframework.stereotype.Component; + +@Component +public class LegalRequirementsClient { + + private final EventHubProperties properties; + private final ObjectMapper objectMapper; + + public LegalRequirementsClient(EventHubProperties properties, ObjectMapper objectMapper) { + this.properties = properties; + this.objectMapper = objectMapper; + } + + public LegalRequirementsUploadResult uploadDriverCard(byte[] fileBytes, String fileName) { + EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements(); + HttpClient client = HttpClient.newBuilder() + .connectTimeout(config.getConnectTimeout()) + .cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL)) + .build(); + try { + String dataPackageId = uploadDataPackage(client, config, fileBytes, fileName); + String xml = downloadXml(client, config, dataPackageId); + return new LegalRequirementsUploadResult(dataPackageId, xml); + } finally { + if (config.isResetSessionAfterUse()) { + resetSessionQuietly(client, config); + } + } + } + + private String uploadDataPackage(HttpClient client, EventHubProperties.LegalRequirements config, byte[] fileBytes, String fileName) { + try { + String payload = objectMapper.createObjectNode() + .put("FileName", fileName) + .putNull("CompressionMediaType") + .put("Data", Base64.getEncoder().encodeToString(fileBytes)) + .toString(); + HttpRequest request = requestBuilder(config.getBaseUrl() + "/DataPackages/Upload", config.getReadTimeout()) + .header("Content-Type", "application/json") + .header("Authorization", basicAuth(config)) + .POST(HttpRequest.BodyPublishers.ofString(payload, StandardCharsets.UTF_8)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() / 100 != 2) { + throw new LegalRequirementsUploadException("LegalRequirements upload failed with status " + response.statusCode()); + } + JsonNode root = objectMapper.readTree(response.body()); + String dataPackageId = text(root, "DataPackageID"); + if (dataPackageId == null) { + dataPackageId = text(root, "DataPackageId"); + } + if (dataPackageId == null && root.has("value") && root.get("value").isObject()) { + dataPackageId = text(root.get("value"), "DataPackageID"); + } + if (dataPackageId == null) { + throw new LegalRequirementsUploadException("LegalRequirements upload response did not contain DataPackageID."); + } + return dataPackageId; + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new LegalRequirementsUploadException("Failed to upload tachograph file to LegalRequirements.", e); + } + } + + private String downloadXml(HttpClient client, EventHubProperties.LegalRequirements config, String dataPackageId) { + try { + HttpRequest request = requestBuilder(config.getBaseUrl() + "/DataPackages/" + dataPackageId + "/Xml", config.getReadTimeout()) + .header("Authorization", basicAuth(config)) + .GET() + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() / 100 != 2) { + throw new LegalRequirementsXmlDownloadException("LegalRequirements XML download failed with status " + response.statusCode()); + } + return response.body(); + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new LegalRequirementsXmlDownloadException("Failed to download processed tachograph XML.", e); + } + } + + private void resetSessionQuietly(HttpClient client, EventHubProperties.LegalRequirements config) { + try { + HttpRequest request = requestBuilder(config.getBaseUrl() + "/LegalRequirementsResults/ResetSession", config.getReadTimeout()) + .header("Authorization", basicAuth(config)) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + client.send(request, HttpResponse.BodyHandlers.discarding()); + } catch (IOException | InterruptedException ignored) { + Thread.currentThread().interrupt(); + } + } + + private HttpRequest.Builder requestBuilder(String url, Duration timeout) { + return HttpRequest.newBuilder(URI.create(url)).timeout(timeout); + } + + private String basicAuth(EventHubProperties.LegalRequirements config) { + String user = config.getUsername() == null ? "" : config.getUsername(); + String password = config.getPassword() == null ? "" : config.getPassword(); + String token = Base64.getEncoder().encodeToString((user + ":" + password).getBytes(StandardCharsets.UTF_8)); + return "Basic " + token; + } + + private String text(JsonNode node, String field) { + if (node == null || !node.has(field) || node.get(field).isNull()) { + return null; + } + return node.get(field).asText(); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsUploadException.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsUploadException.java new file mode 100644 index 0000000..cf20eda --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsUploadException.java @@ -0,0 +1,12 @@ +package at.procon.eventhub.tachographfilesession.service; + +public class LegalRequirementsUploadException extends RuntimeException { + + public LegalRequirementsUploadException(String message) { + super(message); + } + + public LegalRequirementsUploadException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsUploadResult.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsUploadResult.java new file mode 100644 index 0000000..4db0c9a --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsUploadResult.java @@ -0,0 +1,7 @@ +package at.procon.eventhub.tachographfilesession.service; + +public record LegalRequirementsUploadResult( + String dataPackageId, + String xmlContent +) { +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsXmlDownloadException.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsXmlDownloadException.java new file mode 100644 index 0000000..7360d85 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/LegalRequirementsXmlDownloadException.java @@ -0,0 +1,12 @@ +package at.procon.eventhub.tachographfilesession.service; + +public class LegalRequirementsXmlDownloadException extends RuntimeException { + + public LegalRequirementsXmlDownloadException(String message) { + super(message); + } + + public LegalRequirementsXmlDownloadException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionNotFoundException.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionNotFoundException.java new file mode 100644 index 0000000..cab3cf5 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionNotFoundException.java @@ -0,0 +1,10 @@ +package at.procon.eventhub.tachographfilesession.service; + +import java.util.UUID; + +public class TachographFileSessionNotFoundException extends RuntimeException { + + public TachographFileSessionNotFoundException(UUID sessionId) { + super("Tachograph file session not found: " + sessionId); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionRepository.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionRepository.java new file mode 100644 index 0000000..1a8b0b8 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionRepository.java @@ -0,0 +1,17 @@ +package at.procon.eventhub.tachographfilesession.service; + +import at.procon.eventhub.tachographfilesession.model.TachographFileSession; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface TachographFileSessionRepository { + + TachographFileSession save(TachographFileSession session); + + Optional find(UUID sessionId); + + boolean delete(UUID sessionId); + + List list(); +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java new file mode 100644 index 0000000..69aa842 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionService.java @@ -0,0 +1,179 @@ +package at.procon.eventhub.tachographfilesession.service; + +import at.procon.eventhub.config.EventHubProperties; +import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto; +import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto; +import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession; +import at.procon.eventhub.tachographfilesession.model.TachographFileSession; +import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Comparator; +import java.util.HexFormat; +import java.util.List; +import java.util.UUID; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +@Service +public class TachographFileSessionService { + + private final EventHubProperties properties; + private final TachographFileSessionRepository repository; + private final LegalRequirementsClient legalRequirementsClient; + private final TachographXmlParser tachographXmlParser; + private final DriverCardXmlExtractionService extractionService; + + public TachographFileSessionService( + EventHubProperties properties, + TachographFileSessionRepository repository, + LegalRequirementsClient legalRequirementsClient, + TachographXmlParser tachographXmlParser, + DriverCardXmlExtractionService extractionService + ) { + this.properties = properties; + this.repository = repository; + this.legalRequirementsClient = legalRequirementsClient; + this.tachographXmlParser = tachographXmlParser; + this.extractionService = extractionService; + } + + public CreateTachographFileSessionResponse createSession( + MultipartFile file, + String tenantKey, + String sourceInstanceKey, + String sessionLabel + ) { + try { + byte[] fileBytes = file.getBytes(); + validateFile(file, fileBytes); + LegalRequirementsUploadResult uploadResult = legalRequirementsClient.uploadDriverCard(fileBytes, file.getOriginalFilename()); + TachographXmlParser.ParsedTachographXml parsedXml = tachographXmlParser.parseDriverCardXml(uploadResult.xmlContent()); + Instant createdAt = Instant.now(); + Instant expiresAt = createdAt.plus(properties.getTachographFileSession().getTtl()); + TachographFileSessionMetadata metadata = new TachographFileSessionMetadata( + tenant(tenantKey), + sourceInstance(sourceInstanceKey), + blankToNull(sessionLabel), + file.getOriginalFilename(), + sha256(fileBytes), + fileBytes.length, + uploadResult.dataPackageId(), + sha256(uploadResult.xmlContent().getBytes(StandardCharsets.UTF_8)), + true, + null + ); + TachographFileSession session = extractionService.extract(parsedXml, metadata, createdAt, expiresAt); + TachographFileSession saved = repository.save(session); + return new CreateTachographFileSessionResponse(toSummary(saved)); + } catch (Exception e) { + if (e instanceof RuntimeException runtimeException) { + throw runtimeException; + } + throw new IllegalStateException("Failed to create tachograph file session.", e); + } + } + + public TachographFileSessionSummaryDto getSession(UUID sessionId) { + return toSummary(requireSession(sessionId)); + } + + public TachographFileSessionListDriversResponse listDrivers(UUID sessionId) { + TachographFileSession session = requireSession(sessionId); + return new TachographFileSessionListDriversResponse(session.sessionId(), driverSummaries(session)); + } + + public TachographFileDriverDetailDto getDriver(UUID sessionId, String driverKey) { + TachographFileSession session = requireSession(sessionId); + DriverExtractionSession driver = session.driversByKey().get(driverKey); + if (driver == null) { + throw new DriverNotFoundInSessionException(sessionId, driverKey); + } + return new TachographFileDriverDetailDto( + session.sessionId(), + driver.driverKey(), + driver.driver(), + driver.driverCard(), + driver.vehicleRegistrations(), + driver.vehicles(), + driver.cardVehicleUsageIntervals(), + driver.cardActivityIntervals(), + driver.warnings() + ); + } + + public TachographFileSessionDeleteResponse deleteSession(UUID sessionId) { + return new TachographFileSessionDeleteResponse(sessionId, repository.delete(sessionId)); + } + + private TachographFileSession requireSession(UUID sessionId) { + return repository.find(sessionId).orElseThrow(() -> new TachographFileSessionNotFoundException(sessionId)); + } + + private TachographFileSessionSummaryDto toSummary(TachographFileSession session) { + return new TachographFileSessionSummaryDto( + session.sessionId(), + session.metadata().tenantKey(), + session.metadata().sourceInstanceKey(), + session.metadata().sessionLabel(), + session.metadata().originalFileName(), + session.metadata().driverCardFile(), + session.metadata().legalRequirementsDataPackageId(), + session.extractionStats(), + driverSummaries(session), + session.warnings(), + session.createdAt(), + session.expiresAt() + ); + } + + private List driverSummaries(TachographFileSession session) { + return session.driversByKey().values().stream() + .sorted(Comparator.comparing(DriverExtractionSession::driverKey)) + .map(driver -> new TachographFileDriverSummaryDto( + driver.driverKey(), + driver.driver() == null ? null : driver.driver().surname(), + driver.driver() == null ? null : driver.driver().firstNames(), + driver.driverCard() == null ? null : driver.driverCard().cardNation(), + driver.driverCard() == null ? null : driver.driverCard().cardNumber(), + driver.cardActivityIntervals().size(), + driver.cardVehicleUsageIntervals().size() + )) + .toList(); + } + + private void validateFile(MultipartFile file, byte[] fileBytes) { + if (file == null || file.isEmpty() || fileBytes.length == 0) { + throw new IllegalArgumentException("Tachograph file must not be empty."); + } + if (fileBytes.length > properties.getTachographFileSession().getMaxFileSizeBytes()) { + throw new IllegalArgumentException("Tachograph file exceeds the configured size limit."); + } + } + + private String tenant(String tenantKey) { + return blankToNull(tenantKey) == null ? "default" : tenantKey.trim(); + } + + private String sourceInstance(String sourceInstanceKey) { + return blankToNull(sourceInstanceKey) == null ? "legalrequirements-drivercard" : sourceInstanceKey.trim(); + } + + private String blankToNull(String value) { + return value == null || value.isBlank() ? null : value.trim(); + } + + private String sha256(byte[] bytes) { + try { + return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes)); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("SHA-256 digest is not available.", e); + } + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographXmlParser.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographXmlParser.java new file mode 100644 index 0000000..2cdf12c --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographXmlParser.java @@ -0,0 +1,74 @@ +package at.procon.eventhub.tachographfilesession.service; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +@Component +public class TachographXmlParser { + + private static final String JAXP_MAX_OCCUR_LIMIT = "http://www.oracle.com/xml/jaxp/properties/maxOccurLimit"; + + private final Schema schema; + + public TachographXmlParser() { + this.schema = loadSchema(); + } + + public ParsedTachographXml parseDriverCardXml(String xmlContent) { + try { + validate(xmlContent); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(new StringReader(xmlContent))); + String rootName = document.getDocumentElement() == null ? null : document.getDocumentElement().getNodeName(); + if (!"DriverCard".equals(rootName)) { + throw new UnsupportedTachographFileTypeException("Only DriverCard XML documents are supported in phase 1."); + } + return new ParsedTachographXml(document, rootName); + } catch (ParserConfigurationException | IOException | SAXException e) { + throw new TachographXmlValidationException("Failed to parse tachograph XML.", e); + } + } + + private void validate(String xmlContent) { + try { + schema.newValidator().validate(new StreamSource(new StringReader(xmlContent))); + } catch (IOException | SAXException e) { + throw new TachographXmlValidationException("Tachograph XML failed XSD validation.", e); + } + } + + private Schema loadSchema() { + try { + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + factory.setProperty(JAXP_MAX_OCCUR_LIMIT, 100_000); + byte[] content = new ClassPathResource("xsd/Tachograph.xsd").getContentAsByteArray(); + return factory.newSchema(new StreamSource(new ByteArrayInputStream(content))); + } catch (IOException | SAXException e) { + throw new IllegalStateException("Failed to load Tachograph XSD schema.", e); + } + } + + public record ParsedTachographXml( + Document document, + String rootElementName + ) { + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographXmlValidationException.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographXmlValidationException.java new file mode 100644 index 0000000..16c8bf9 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/TachographXmlValidationException.java @@ -0,0 +1,12 @@ +package at.procon.eventhub.tachographfilesession.service; + +public class TachographXmlValidationException extends RuntimeException { + + public TachographXmlValidationException(String message) { + super(message); + } + + public TachographXmlValidationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/UnsupportedTachographFileTypeException.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/UnsupportedTachographFileTypeException.java new file mode 100644 index 0000000..f5dd343 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/UnsupportedTachographFileTypeException.java @@ -0,0 +1,8 @@ +package at.procon.eventhub.tachographfilesession.service; + +public class UnsupportedTachographFileTypeException extends RuntimeException { + + public UnsupportedTachographFileTypeException(String message) { + super(message); + } +} diff --git a/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleKeyFactory.java b/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleKeyFactory.java new file mode 100644 index 0000000..b286881 --- /dev/null +++ b/src/main/java/at/procon/eventhub/tachographfilesession/service/VehicleKeyFactory.java @@ -0,0 +1,32 @@ +package at.procon.eventhub.tachographfilesession.service; + +import org.springframework.stereotype.Component; + +@Component +public class VehicleKeyFactory { + + public String createRegistrationKey(String registrationNation, String registrationNumber) { + String normalizedNation = normalize(registrationNation, "UNKNOWN"); + String normalizedNumber = normalize(registrationNumber, "UNKNOWN"); + return normalizedNation + ":" + normalizedNumber; + } + + public String createSourceVehicleRegistrationId(String registrationKey) { + return "VR:" + registrationKey; + } + + public String createVehicleKey(String vin) { + return normalize(vin, null); + } + + public String createSourceVehicleId(String vehicleKey) { + return vehicleKey == null ? null : "VIN:" + vehicleKey; + } + + private String normalize(String value, String fallback) { + if (value == null || value.isBlank()) { + return fallback; + } + return value.trim(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 24e4b4d..3f4d816 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -121,6 +121,18 @@ eventhub: initial-occurred-to: "2026-04-10T00:00:00+01:00" run-initial-on-startup: true + tachograph-file-session: + ttl: 4h + max-sessions: 100 + max-file-size-bytes: 20971520 + legal-requirements: + base-url: ${LEGAL_REQUIREMENTS_BASE_URL:https://legalrequirements.services.bytebar.eu/ODataV4/LR} + username: ${LEGAL_REQUIREMENTS_USERNAME:} + password: ${LEGAL_REQUIREMENTS_PASSWORD:} + connect-timeout: 20s + read-timeout: 2m + reset-session-after-use: true + esper-poc: activity-merge-mode: JAVA shift-resolution-mode: JAVA diff --git a/src/main/resources/xsd/Tachograph.xsd b/src/main/resources/xsd/Tachograph.xsd new file mode 100644 index 0000000..6f869a1 --- /dev/null +++ b/src/main/resources/xsd/Tachograph.xsd @@ -0,0 +1,7292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id=0x000200,0x000202;generation=1,* + ICC (integrated circuit card) information + + + + + + + id=0x000201,0x000203;generation=1,* + + + + + + + + + id=0x000500,0x000502;generation=1,* + IC (integrated circuit) information + + + + + + + id=0x000501,0x000503;generation=1,* + + + + + + + + + id=0x2F0000,0x2F0002;generation=1,* + +The elementary file EF DIR shall contain the following application related data objects: ‘61 08 4F 06 FF 54 41 43 48 4F 61 08 4F 06 FF 53 4D 52 44 54’ + + + + + + + + + + + + + id=0x2F0001,0x2F0003;generation=1,* + + + + + + + + + id=0x2F0100,0x2F0102;generation=1,* + +The elementary file EF ATR/INFO shall be present if the tachograph card indicates in its ATR that it supports extended length fields. In this case the EF ATR/INFO shall contain the extended length information data object (DO‘7F66’) as specified in ISO/IEC 7816-4:2013 clause 12.7.1. + + + + + + + + + + value=-1 + + + + + + + + id=0x2F0101,0x2F0103;generation=1,* + + + + + + + + + id=0x000600,0x000602;generation=1,* + +The elementary file EF Extended_Length shall be present if the tachograph card indicates in its ATR that it supports extended length fields. In this case the EF shall contain the following data object: ‘02 01 xx’ where the value ‘xx’ indicates whether extended length fields are supported for the T = 1 and / or T = 0 protocol. + +The value ‘01’ indicates extended length field support for the T = 1 protocol. +The value ‘10’ indicates extended length field support for the T = 0 protocol. +The value ‘11’ indicates extended length field support for the T = 1 and the T = 0 protocol. + + + + + + + + + + + + + id=0x000601,0x000603;generation=1,* + + + + + + + + + + + +Identification of a vehicle, unique for Europe (VRN and Member State). + + + + + vehicleRegistrationNation is the nation where the vehicle is registered. + + + + + vehicleRegistrationNumber is the registration number of the vehicle (VRN). + + + + + + + +Identification of a vehicle, unique for Europe (VRN and Member State). + + + + + generation=1 + vehicleRegistrationNation is the nation where the vehicle is registered. + + + + + vehicleRegistrationNumber is the registration number of the vehicle (VRN). + + + + + + + +Registration number of the vehicle (VRN). The registration number is assigned by the vehicle licensing authority. + + + + + codePage specifies a character set defined in Chapter 4. + +codePage ::= INTEGER(0..255) + + + + + vehicleRegNumber is a VRN encoded using the specified character set. +Value assignment: Country specific. + + + + + + + + + + + + + +The certificate of a public key issued by a Certification Authority. + +Generation 1: +Certificate ::= OCTET STRING(SIZE(194)) + +Value assignment: digital signature with partial recovery of a CertificateContent according to Appendix 11 common security mechanisms: Signature (128 bytes) || Public Key remainder (58 bytes) || Certification Authority Reference (8 bytes). + +Generation 2: +Certificate ::= OCTET STRING(SIZE(204..341)) + +Value assignment: See Appendix 11 + + + + + generation=1 + + + + + + + + + + + + + + + + + + + + generation=* + + + + + + + + +Generation 1: +Certificate of the public key of a card. + + + + + + + + +The certificate of the public key of a member state issued by the European certification authority. + + + + + + + + +Generation 2: +Certificate of the card public key for mutual authentication with a VU. The structure of this certificate is specified in Appendix 11. + + + + + + + + +Generation 2: +Certificate of the card public key for signature. The structure of this certificate is specified in Appendix 11. + + + + + + + + +Generation 2: +The link certificate between European Root CA key pairs. + + + + + + + + +Certificate of the public key of a vehicle unit. + + + + + + + + +Code fully identifying a tachograph card. + + + + + cardType is the type of the tachograph card. + + + + + cardIssuingMemberState is the code of the Member State having issued the card. + + + + + cardNumber is the card number. + + + + + + + +Generation 2: +Code fully identifying a tachograph card and its generation. + + + + + + cardType is the type of the tachograph card. + + + + + cardIssuingMemberState is the code of the Member State having issued the card. + + + + + cardNumber is the card number. + + + + + generation=2.. + generation indicates the generation of the tachograph card used. + + + + + + + +An address. + + + + + codePage specifies a character set defined in Chapter 4. + +codePage ::= INTEGER(0..255) + + + + + address is an address encoded using the specified character set. + + + + + + + + + + + + + +A card number as defined by definition g). + + + + + The first sequence of the choice is suitable to code a driver card number. + + + + driverIdentification is the unique identification of a driver in a Member State. + + + + + + + + + + + cardReplacementIndex is the card replacement index. + + + + + cardRenewalIndex is the card renewal index. + + + + + + the second sequence of the choice is suitable to code workshop, control, and company card numbers. + + + + ownerIdentification is the unique identification of a company or a workshop or a control body within a Member State. + + + + + + + + + + + cardConsecutiveIndex is the card consecutive index. + + + + + cardReplacementIndex is the card replacement index. + + + + + cardReplacementIndex is the card replacement index. + + + + + + + + +Information, stored in a vehicle unit, related to one company lock (Annex 1B requirement 104 and Annex 1C requirement 128). + +Generation 2: +Instead of companyCardNumber the generation 2 data structure makes use of the following data element: companyCardNumberAndGeneration. + + + + + lockInTime is the date and time of lock-in. + + + + + lockOutTime is the date and time of lock-out. + + + + + companyName is the company name related with the lock-in. + + + + + companyAddress is the company address related with the lock-in. + + + + + Generation 1: +companyCardNumber identifies the card used at lock-in. + +Generation 2: +companyCardNumberAndGeneration identifies the card including its generation used at lock-in. + + + + + + + +Information, stored in a vehicle unit, related to its last download (Annex 1B requirement 105 and Annex 1C requirement 129). + +Generation 2: +Instead of fullCardNumber the generation 2 data structure makes use of the following data element: fullCardNumberAndGeneration. + + + + + downloadingTime is the date and time of downloading. + + + + + Generation 1: +fullCardNumber identifies the card used to authorise the download. + +Generation 2: +fullCardNumberAndGeneration identifies the card including its generation used to authorise the download. + + + + + companyOrWorkshopName is the company or workshop name. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to company locks (Annex 1B requirement 104). + + + + + noOfLocks is the number of locks listed in vuCompanyLocksRecords. + +noOfLocks ::= INTEGER(0..20) + + + + + + + + vuCompanyLocksRecords is the set of company locks records. + + + + + + + +A name. + + + + + codePage specifies a character set defined in Chapter 4. + +codePage ::= INTEGER(0..255) + + + + + name is a name encoded using the specified character set. + + + + + + + + + + + + + +Oldest and latest dates for which a vehicle unit holds data related to drivers activities (Annex 1B requirements 081, 084 or 087 and Annex 1C requirements 102, 105, 108). + + + + + minDownloadableTime is the oldest card insertion or activity change or place entry date and time stored in the VU. + + + + + maxDownloadableTime is the latest card withdrawal or activity change or place entry date and time stored in the VU. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to controls performed using this VU (Annex 1B requirement 102). + + + + + noOfControls is the number of controls listed in vuControlActivityRecords. + +noOfControls ::= INTEGER(0..20) + + + + + + + + vuControlActivityRecords is the set of control activity records. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to insertion and withdrawal cycles of driver cards or of workshop cards in the vehicle unit (Annex 1B requirement 081 and Annex 1C requirement 103). + + + + + noOfIWRecords is the number of records in the set vuCardIWRecords. + +noOfIWRecords ::= INTEGER(0..2^16-1) + + + + + vuCardIWRecords is a set of records related to card insertion withdrawal cycles. + + + + + + + +Information, stored in a vehicle unit, related to an insertion and withdrawal cycle of a driver card or of a workshop card in the vehicle unit (Annex 1B requirement 081 and Annex 1C requirement 102). + +Generation 2: +Instead of fullCardNumber the generation 2 data structure makes use of the following data element. + + + + + cardHolderName is the driver or workshop card holder's surname and first names as stored in the card. + + + + + Generation 1: +fullCardNumber is the type of card, its issuing Member State and its card number as stored in the card. + +Generation 2: +fullCardNumberAndGeneration is the type of card, its issuing Member State, its card number and generation as stored in the card. + + + + + cardExpiryDate is the card's expiry date as stored in the card. + + + + + cardInsertionTime is the insertion date and time. + + + + + vehicleOdometerValueAtInsertion is the vehicle odometer value at card insertion. + + + + + cardSlotNumber is the slot in which the card is inserted. + + + + + cardWithdrawalTime is the withdrawal date and time. + + + + + vehicleOdometerValueAtWithdrawal is the vehicle odometer value at card withdrawal. + + + + + previousVehicleInfo contains information about the previous vehicle used by the driver, as stored in the card. + + + + + manualInputFlag is a flag identifying if the cardholder has manually entered driver activities at card insertion. + + + + + + + +The surname and first name(s) of a card holder. + + + + + holderSurname is the surname (family name) of the holder. This surname does not include titles. +Value assignment: When a card is not personal, holderSurname contains the same information as companyName or workshopName or controlBodyName. + + + + + holderFirstNames is the first name(s) and initials of the holder. + + + + + + + +Information related to the vehicle previously used by a driver when inserting his card in a vehicle unit (Annex 1B requirement 081 and Annex 1C requirement 102). + +Generation 2: +In addition to generation 1 the following data element is used: vuGeneration + + + + + vehicleRegistrationIdentification is the VRN and the registering Member State of the vehicle. + + + + + cardWithdrawalTime is the card withdrawal date and time. + + + + + generation=2.. + vuGeneration identifies the generation of the vehicle unit. + + + + + + + +Information, stored in a vehicle unit, related to a control performed using this VU (Annex 1B requirement 102 and Annex 1C requirement 126). + +Generation 2: +Instead of controlCardNumber the generation 2 data structure makes use of the following data element: controlCardNumberAndGeneration. + + + + + controlType is the type of the control. + + + + + controlTime is the date and time of the control. + + + + + Generation 1: +ControlCardNumber identifies the control card used for the control. + +Generation 2: +controlCardNumberAndGeneration identifies the control card including its generation used for the control. + + + + + downloadPeriodBeginTime is the begin time of the downloaded period, in case of downloading. + + + + + downloadPeriodEndTime is the end time of the downloaded period, in case of downloading. + + + + + + + +Generation 1: +Information, stored in a VU, related to changes of activity and/or changes of driving status and/or changes of card status for a given calendar day (Annex 1B requirement 084 and Annex 1C requirement 105, 106, 107) and to slots status at 00:00 that day. + + + + + noOfActivityChanges is the number of ActivityChangeInfo words in the activityChangeInfos set. + +noOfActivityChanges ::= INTEGER(0..1440) + + + + + activityChangeInfos is the set of ActivityChangeInfo words stored in the VU for the day. It always includes two ActivityChangeInfo words giving the status of the two slots at 00.00 that day. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to a place where a driver begins or ends a daily work period (Annex 1B requirement 087 and Annex 1C requirement 108 and 110). + +Generation 2, version 1: +Instead of fullCardNumber, the generation 2 data structure makes use of the following data element: fullCardNumberAndGeneration. + +Generation 2, version 2: +Instead of placeRecord, the generation 2 version 2 data structure makes use of the following data element: placeAuthRecord + + + + + Generation 1: +fullCardNumber is the driver's card type, card issuing Member State and card number. + +Generation 2: +fullCardNumberAndGeneration is the type of card, its issuing Member State, its card number and generation as stored in the card. + + + + + +Generation 1 / Generation 2, version 1: +placeRecord contains the information related to the place entered. + +Generation 2, version 2: +placeAuthRecord contains the information related to the place entered, the recorded position, GNSS authentication status and position determination time. + + + + + + + + +Information related to a place where a daily work period begins or ends (Annex 1C requirements 108, 271, 296, 324, and 347). + +Generation 2: +In addition to Generation 1 the following component is used: entryGNSSPlaceRecord. + + + + + entryTime is a date and time related to the entry. + + + + + entryTypeDailyWorkPeriod is the type of entry. + + + + + dailyWorkPeriodCountry is the country entered. + + + + + dailyWorkPeriodRegion is the region entered. + + + + + vehicleOdometerValue is the odometer value at the time of place entry. + + + + + generation=2.. + +entryGNSSPlaceRecord is the recorded location and time. +entryGNSSPlaceAuthRecord is the recorded location, GNSS authentication status and time. + + + + + + + +Generation 2, version 1: +Information, stored in a vehicle unit, related to the GNSS position of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirement 108, 110). + +Generation 2, version 2: +Information, stored in a vehicle unit, related to the GNSS position of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirement 108, 110). +In Generation 2 version 2, instead of gnssPlaceRecord, the gnssPlaceAuthRecord is used, which contains the GNSS authentication status in addition. + + + + + timeStamp is the date and time when the accumulated driving time reaches a multiple of three hours. + + + + + cardNumberAndGenDriverSlot identifies the card including its generation which is inserted in the driver slot. + + + + + cardNumberAndGenCodriverSlot identifies the card including its generation which is inserted in the co-driver slot. + + + + + +gnssPlaceRecord contains information related to the position of the vehicle. +gnssPlaceAuthRecord contains information related to the position of the vehicle. + + + + + vehicleOdometerValue is the odometer value when the accumulated driving time reaches a multiple of three hours. + + + + + + + +Generation 2: +Information related to the GNSS position of the vehicle (Annex 1C requirements 108, 109, 110, 296, 305, 347, and 353). + +Generation 2, version 2: +Information related to the GNSS position of the vehicle (Annex IC requirements 108, 109, 110, 296, 306a, 306c, 306e, 306g, 356a, 356c, 356e and 356g). + + + + + timeStamp is the date and time when the GNSS position of the vehicle was determined. + + + + + gnssAccuracy is the accuracy of the GNSS position data. + + + + + geoCoordinates is the recorded location using GNSS. + + + + + generation=3.. + authenticationStatus is the authentication status of the GNSS position when it was determined. + + + + + + + +Generation 2: +The geo-coordinates are encoded as integers. These integers are multiples of the ±DDMM.M encoding for the latitude and ±DDDMM.M for the longitude. Here ±DD respectively ±DDD denotes the degrees and MM.M the minutes. + + + + + latitude is encoded as a multiple (factor 10) of the ±DDMM.M representation. + +latitude ::= INTEGER(-90000..90001) + +Note: Latitude is converted to degrees by FKXMLtoDB. + + + + + longitude is encoded as a multiple (factor 10) of the ±DDDMM.M representation. + +longitude ::= INTEGER(-180000..180001) + +Note: Longitude is converted to degrees by FKXMLtoDB. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to places where drivers begin or end a daily work period (Annex 1B requirement 087 and Annex 1C requirement 108 and 110). + + + + + noOfPlaceRecords is the number of records listed in the vuPlaceDailyWorkPeriodRecords set. + +noOfPlaceRecords ::= INTEGER(0..255) + + + + + vuPlaceDailyWorkPeriodRecords is a set of place related records. + + + + + + + +Information, stored in a driver card, a workshop card or a vehicle unit, related to a specific condition (requirements Annex 1C 130, 276, 301, 328, and 355). + + + + + entryTime is the date and time of the entry. + + + + + specificConditionType is the code identifying the specific condition. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to specific conditions. + + + + + noOfSpecificConditionRecords is the number of records listed in the specificConditionRecords set. + +noOfSpecificConditionRecords ::= INTEGER(0..2^16-1) + + + + + specificConditionRecords is a set of specific conditions related records. + + + + + + + +Generation 2, version 2: +Information, stored in a vehicle unit, related to border crossings of the vehicle when the latter has crossed the border of a country (Annex IC requirement 133a and 133b). + + + + + cardNumberAndGenDriverSlot identifies the card including its generation which is inserted in the driver slot. + + + + + cardNumberAndGenCodriverSlot identifies the card including its generation which is inserted in the co-driver slot. + + + + + countryLeft is the country which was left by the vehicle, based on the last available position before the border crossing has been detected. +‘Rest of the World’ (NationNumeric code ‘FF’H) shall be used when the vehicle unit is not able to determine the country where the vehicle is located (e.g. the current country is not part of the stored digital maps). + + + + + countryEntered is the country into which the vehicle has entered. +‘Rest of the World’ (NationNumeric code ‘FF’H) shall be used when the vehicle unit is not able to determine the country where the vehicle is located (e.g. the current country is not part of the stored digital maps). + + + + + gnssPlaceAuthRecord contains information related to the position of the vehicle when the border crossing was detected, and its authentication status. + + + + + vehicleOdometerValue is the odometer value when the vehicle unit has detected that the vehicle has crossed the border of a country. + + + + + + + +Generation 2, version 2: +Information, stored in the vehicle unit, related to a load/unload operation entered (Annex IC requirements 133e, 133f and 133g). + + + + + timeStamp is the date and time when the load/unload operation was entered. + + + + + operationType is the type of the operation entered (load, unload, or simultaneous load/unload). + + + + + cardNumberAndGenDriverSlot identifies the card including its generation which is inserted in the driver slot. + + + + + cardNumberAndGenCodriverSlot identifies the card including its generation which is inserted in the co-driver slot. + + + + + gnssPlaceAuthRecord contains information related to the position of the vehicle, and its authentication status. + + + + + vehicleOdometerValue is the odometer value related to the load/unload operation. + + + + + + + +Generation 2: +Manufacturer specific error codes simplify the error analysis and maintenance of vehicle units. + + + + + manufacturerCode identifies the manufacturer of the Vehicle Unit. + + + + + manufacturerSpecificErrorCode is an error code specific to the manufacturer. + +manufacturerSpecificErrorCode ::= OCTET STRING(SIZE(3)) + + + + + + + + + + + + +Information, stored in a vehicle unit, related to a fault (Annex 1B requirement 096 and Annex 1C requirement 118). + +Generation 2: +In addition to generation 1 the following data element is used: manufacturerSpecificEventFaultData. + +Instead of cardNumberDriverSlotBegin, cardNumberCodriverSlotBegin, cardNumberDriverSlotEnd, and cardNumberCodriverSlotEnd the generation 2 data structure makes use of the following data elements: cardNumberAndGenDriverSlotBegin, cardNumberAndGenCodriverSlotBegin, cardNumberAndGenDriverSlotEnd, cardNumberAndGenCodriverSlotEnd. + + + + + faultType is the type of recording equipment fault. + + + + + faultRecordPurpose is the purpose for which this fault has been recorded. + + + + + faultBeginTime is the date and time of beginning of fault. + + + + + faultEndTime is the date and time of end of fault. + + + + + Generation 1: +cardNumberDriverSlotBegin identifies the card inserted in the driver slot at the beginning of the fault. + +Generation 2: +cardNumberAndGenDriverSlotBegin identifies the card including its generation which is inserted in the driver slot at the beginning of the fault. + + + + + Generation 1: +cardNumberCodriverSlotBegin identifies the card inserted in the co-driver slot at the beginning of the fault. + +Generation 2: +cardNumberAndGenCodriverSlotBegin identifies the card including its generation which is inserted in the co-driver slot at the beginning of the fault. + + + + + Generation 1: +cardNumberDriverSlotEnd identifies the card inserted in the driver slot at the end of the fault. + +Generation 2: +cardNumberAndGenDriverSlotEnd identifies the card including its generation which is inserted in the driver slot at the end of the fault. + + + + + Generation 1: +cardNumberCodriverSlotEnd identifies the card inserted in the co-driver slot at the end of the fault. + +Generation 2: +cardNumberAndGenCodriverSlotEnd identifies the card including its generation which is inserted in the co-driver slot at the end of the fault. + + + + + generation=2.. + manufacturerSpecificEventFaultData contains additional, manufacturer specific information about the fault. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to faults (Annex 1B requirement 096). + + + + + noOfVuFaults is the number of faults listed in the vuFaultRecords set. + +noOfVuFaults ::= INTEGER(0..255) + + + + + vuFaultRecords is a set of faults records. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to events (Annex 1B requirement 094 except over speeding event). + + + + + noOfVuEvents is the number of events listed in the vuEventRecords set. + +noOfVuEvents ::= INTEGER(0..255) + + + + + vuEventRecords is a set of events records. + + + + + + + +Information, stored in a vehicle unit, related to an event (Annex 1B requirement 094 and Annex 1C requirement 117 except over speeding event). + +Generation 2: +In addition to generation 1 the following data elements are used: manufacturerSpecificEventFaultData. +Instead of cardNumberDriverSlotBegin, cardNumberCodriverSlotBegin, cardNumberDriverSlotEnd, and cardNumberCodriverSlotEnd the generation 2 data structure makes use of the following data elements: cardNumberAndGenDriverSlotBegin, cardNumberAndGenCodriverSlotBegin, cardNumberAndGenDriverSlotEnd, cardNumberAndGenCodriverSlotEnd. + +If the event is a time conflict the eventBeginTime and eventEndTime are to be interpreted as follows: +eventBeginTime is the recording equipment date and time. +eventEndTime is the GNSS date and time. + + + + + eventType is the type of the event. + + + + + eventRecordPurpose is the purpose for which this event has been recorded. + + + + + eventBeginTime is the date and time of beginning of event. + + + + + eventEndTime is the date and time of end of event. + + + + + Generation 1: +cardNumberDriverSlotBegin identifies the card inserted in the driver slot at the beginning of the event. + +Generation 2: +cardNumberAndGenDriverSlotBegin identifies the card including its generation which is inserted in the driver slot at the beginning of the event. + + + + + Generation 1: +cardNumberCodriverSlotBegin identifies the card inserted in the co-driver slot at the beginning of the event. + +Generation 2: +cardNumberAndGenCodriverSlotBegin identifies the card including its generation which is inserted in the co-driver slot at the beginning of the event. + + + + + Generation 1: +cardNumberDriverSlotEnd identifies the card inserted in the driver slot at the end of the event. + +Generation 2: +cardNumberAndGenDriverSlotEnd identifies the card including its generation which is inserted in the driver slot at the end of the event. + + + + + Generation 1: +cardNumberCodriverSlotEnd identifies the card inserted in the co-driver slot at the end of the event. + +Generation 2: +cardNumberAndGenCodriverSlotEnd identifies the card including its generation which is inserted in the co-driver slot at the end of the event. + + + + + similarEventsNumber is the number of similar events that day. + + + + + generation=2.. + manufacturerSpecificEventFaultData contains additional, manufacturer specific information about the fault. + + + + + + + +Generation 2: +Information, stored in a vehicle unit, related to Power Supply Interruption events (Annex 1C requirement 117). + + + + + eventType is the type of the event. + + + + + eventRecordPurpose is the purpose for which this event has been recorded. + + + + + eventBeginTime is the date and time of beginning of event. + + + + + eventEndTime is the date and time of end of event. + + + + + cardNumberAndGenDriverSlotBegin identifies the card including its generation which is inserted in the driver slot at the beginning of the event. + + + + + cardNumberAndGenCodriverSlotBegin identifies the card including its generation which is inserted in the co-driver slot at the beginning of the event. + + + + + cardNumberAndGenDriverSlotEnd identifies the card including its generation which is inserted in the driver slot at the end of the event. + + + + + cardNumberAndGenCodriverSlotEnd identifies the card including its generation which is inserted in the co-driver slot at the end of the event. + + + + + similarEventsNumber is the number of similar events that day. + + + + + + + +Information, stored in a vehicle unit, related to over speeding events since the last over speeding control (Annex 1B requirement 095 and Annex 1C requirement 117). + + + + + lastOverspeedControlTime is the date and time of the last over speeding control. + + + + + firstOverspeedSince is the date and time of the first over speeding following this over speeding control. + + + + + numberOfOverspeedSince is the number of over speeding events since the last over speeding control. + + + + + + + +Information, stored in a vehicle unit, related to over speeding events (Annex 1B requirement 094 and Annex 1C requirement 117). + +Generation 2: +Instead of cardNumberDriverSlotBegin, the generation 2 data structure makes use of the following data element: cardNumberAndGenDriverSlotBegin. + + + + + eventType is the type of the event. + + + + + eventRecordPurpose is the purpose for which this event has been recorded. + + + + + eventBeginTime is the date and time of beginning of event. + + + + + eventEndTime is the date and time of end of event. + + + + + maxSpeedValue is the maximum speed measured during the event. + + + + + averageSpeedValue is the arithmetic average speed measured during the event. + + + + + Generation 1: +cardNumberDriverSlotBegin identifies the card inserted in the driver slot at the beginning of the event. + +Generation 2: +cardNumberAndGenDriverSlotBegin identifies the card including its generation which is inserted in the driver slot at the beginning of the event. + + + + + similarEventsNumber is the number of similar events that day. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to over speeding events (Annex 1B requirement 094). + + + + + noOfVuOverSpeedingEvents is the number of events listed in the vuOverSpeedingEventRecords set. + +noOfVuOverSpeedingEvents ::= INTEGER(0..255) + + + + + vuOverSpeedingEventRecords is a set of over speeding events records. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to time adjustments performed outside the frame of a regular calibration (Annex 1B requirement 101). + + + + + noOfVuTimeAdjRecords is the number of records in vuTimeAdjustmentRecords. + +noOfVuTimeAdjRecords ::= INTEGER(0..6) + + + + + vuTimeAdjustmentRecords is a set of time adjustment records. + + + + + + + +Information, stored in a vehicle unit, related a time adjustment performed outside the frame of a regular calibration (Annex 1B requirement 101 and Annex 1C requirement 124 and 125). + +Generation 2: +Instead of workshopCardNumber the generation 2 data structure makes use of the following data element: workshopCardNumberAndGeneration. + + + + + oldTimeValue, newTimeValue are the old and new values of date and time. + + + + + oldTimeValue, newTimeValue are the old and new values of date and time. + + + + + workshopName, workshopAddress are the workshop name and address. + + + + + workshopName, workshopAddress are the workshop name and address. + + + + + Generation 1: +workshopCardNumber identifies the workshop card used to perform the time adjustment. + +Generation 2: +workshopCardNumberAndGeneration identifies the workshop card including its generation used to perform the time adjustment. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + codePage specifies a character set defined in Chapter 4. + +codePage ::= INTEGER(0..255) + + + + + + + + + + + + + + + + + + + + codePage specifies a character set defined in Chapter 4. + +codePage ::= INTEGER(0..255) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + High resolution odometer value. Divide by 200 to get value in km. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to the detailed speed of the vehicle. + + + + + vuDetailedSpeedBlocks is the set of detailed speed blocks. + + + + + + + + + vuDetailedSpeedBlocks is the set of detailed speed blocks. + + + + + + + +Information, stored in a vehicle unit, related to the vehicle's detailed speed for a minute during which the vehicle has been moving (Annex 1B requirement 093 and Annex 1C requirement 116). + + + + + speedBlockBeginDate is the date and time of the first speed value within the block. + + + + + speedsPerSecond is the chronological sequence of measured speeds every seconds for the minute starting at speedBlockBeginDate (included). + + + + + + + + + + + + +Information, stored in a vehicle unit, related to the vehicle's detailed speed for a minute during which the vehicle has been moving (Annex 1B requirement 093 and Annex 1C requirement 116). + + + + + speedBlockBeginDate is the date and time of the first speed value within the block. + + + + + speedsPerSecond is the chronological sequence of measured speeds every seconds for the minute starting at speedBlockBeginDate (included). + + + + + + + + +This data type enables to code, within a two bytes word, a slot status at 00:00 and/or a driver status at 00:00 and/or changes of activity and/or changes of driving status and/or changes of card status for a driver or a co-driver. This data type is related to Annex 1C requirements 105, 266, 291, 320, 321, 343, and 344. + +Note: This type is used instead of type ActivityChangeInfo_. + + + + + + + + + Time of the change on the given day. + + + + + + + + +Information, stored in a vehicle unit, related to the identification of the vehicle unit (Annex 1B requirement 075 and Annex 1C requirement 93 and 121). + +Generation 2: +In addition to generation 1 the following data element are used: vuGeneration, vuAbility. + + + + + vuManufacturerName is the name of the manufacturer of the vehicle unit. + + + + + vuManufacturerAddress is the address of the manufacturer of the vehicle unit. + + + + + vuPartNumber is the part number of the vehicle unit. + + + + + vuSerialNumber is the serial number of the vehicle unit. + + + + + vuSoftwareIdentification identifies the software implemented in the vehicle unit. + + + + + vuManufacturingDate is the manufacturing date of the vehicle unit. + + + + + vuApprovalNumber is the type approval number of the vehicle unit. + + + + + generation=2.. + vuGeneration identifies the generation of the vehicle unit. + + + + + generation=2.. + vuAbility provides information whether the VU supports generation 1 tachograph cards or not. + + + + + generation=3.. + vuDigitalMapVersion is the version of the digital map stored in the vehicle unit (only present in version 2). + + + + + + + +Unique identification of an equipment. It can also be used as an equipment Public Key Identifier. + + + + + serialNumber is a serial number for the equipment, unique for the manufacturer, the equipment's type and the month below. + +serialNumber ::= INTEGER(0..2^32-1) + + + + + monthYear is the identification of the month and the year of manufacturing (or of serial number assignment). +Value assignment: BCD coding of Month (two digits) and Year (two last digits). + + + + + + + + + + Generation 1: +type is an identifier of the type of equipment. + +type ::= OCTET STRING(SIZE(1)) + +Value assignment: manufacturer specific, with ‘FFh’ reserved value. + +Generation 2: +type indicates the type of equipment. + +type ::= EquipmentType + + + + + manufacturerCode is the numerical code of the manufacturer of the equipment. + + + + + + + +Serial number of the vehicle unit (Annex 1B requirement 075 and Annex 1C requirement 93). + + + + + + + + +Information, stored in a vehicle unit, related to the software installed. + + + + + vuSoftwareVersion is the software version number of the Vehicle Unit. + + + + + vuSoftInstallationDate is the software version installation date. + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to the identification of the motion sensor paired with the vehicle unit (Annex 1B requirement 079). + + + + + sensorSerialNumber is the serial number of the motion sensor currently paired with the vehicle unit. + + + + + sensorApprovalNumber is the approval number of the motion sensor currently paired with the vehicle unit. + + + + + sensorPairingDateFirst is the date of the first pairing with a vehicle unit of the motion sensor currently paired with the vehicle unit. + + + + + + + +Generation 2: +Information, stored in a vehicle unit, related to the identification of a motion sensor paired with the vehicle unit (Annex 1C requirement 97). + + + + + sensorSerialNumber is the serial number of a motion sensor paired with the vehicle unit. + + + + + sensorApprovalNumber is the approval number of this motion sensor. + + + + + sensorPairingDate is a date of pairing of this motion sensor with the vehicle unit. + + + + + + + +Serial number of the motion sensor. + + + + + + + + +Generation 2: +Information, stored in a vehicle unit, related to the identification of the external GNSS facility coupled with the vehicle unit (Annex 1C requirement 100). + + + + + sensorSerialNumber is the serial number of the external GNSS facility coupled with the vehicle unit. + + + + + sensorApprovalNumber is the approval number of this external GNSS facility. + + + + + sensorCouplingDate is a date of coupling of this external GNSS facility with the vehicle unit. + + + + + + + +Generation 2: +This type is used to store the serial number of the GNSS receiver both when it is inside the VU and when it is outside the VU. +Serial number of the GNSS receiver. + + + + + + + + +Generation 1: +Information, stored in a vehicle unit, related to the calibrations of the recording equipment (Annex 1B requirement 098). + + + + + noOfVuCalibrationRecords is the number of records contained in the vuCalibrationRecords set. + +noOfVuCalibrationRecords ::= INTEGER(0..255) + + + + + vuCalibrationRecords is the set of calibration records. + + + + + + + +Information, stored in a vehicle unit, related a calibration of the recording equipment (Annex 1B requirement 098 and Annex 1C requirement 119 and 120). + +Generation 2, version 1: +In addition to generation 1 the following data element is used: sealDataVU. + +Generation 2, version 2: +In addition to generation 1 the following data element is used: + + + + + calibrationPurpose is the purpose of the calibration. + + + + + workshopName, workshopAddress are the workshop name and address. + + + + + workshopName, workshopAddress are the workshop name and address. + + + + + workshopCardNumber identifies the workshop card used during the calibration. + + + + + workshopCardExpiryDate is the card expiry date. + + + + + vehicleIdentificationNumber is the VIN. + + + + + vehicleRegistrationIdentification contains the VRN and registering Member State. + + + + + wVehicleCharacteristicConstant is the characteristic coefficient of the vehicle. + + + + + kConstantOfRecordingEquipment is the constant of the recording equipment. + + + + + lTyreCircumference is the effective circumference of the wheel tyres. + + + + + tyreSize is the designation of the dimension of the tyres mounted on the vehicle. + + + + + authorisedSpeed is the authorised speed of the vehicle. + + + + + oldOdometerValue, newOdometerValue are the old and new values of the odometer. + + + + + oldOdometerValue, newOdometerValue are the old and new values of the odometer. + + + + + oldTimeValue, newTimeValue are the old and new values of date and time. + + + + + oldTimeValue, newTimeValue are the old and new values of date and time. + + + + + nextCalibrationDate is the date of the next calibration of the type specified in CalibrationPurpose to be carried out by the authorised inspection authority. + + + + + generation=3.. + sensorSerialNumber is the serial number of the motion sensor paired with the vehicle unit at the end of the calibration. + + + + + generation=3.. + sensorGNSSSerialNumber is the serial number of the external GNSS facility coupled with the vehicle unit at the end of the calibration (if any). + + + + + generation=3.. + rcmSerialNumber is the serial number of the remote communication facility coupled with the vehicle unit at the end of the calibration (if any). + + + + + generation=2.. + sealDataVu gives information about the seals that are attached to different components of the vehicle. + + + + + generation=3.. + byDefaultLoadType is the by-default load type of the vehicle (only present in version 2). + + + + + generation=3.. + calibrationCountry is the country in which the calibration has been performed. + + + + + generation=3.. + calibrationCountryTimestamp is the date and time when the position used to determine the country in which the calibration has been performed was provided by the GNSS receiver. + + + + + + + +Generation 2: +This data type stores information about the seals that are attached to the different components of a vehicle and is intended for storage in a Vehicle Unit. + + + + + sealRecords is a set of seal records. If there are less than 5 seals available the value of the EquipmentType in all unused sealRecords shall be set to 16, i.e. unused. + + + + + + + +Generation 2: +This data type stores information about a seal that is attached to a component. This data type is related to Annex 1C requirement 337. + + + + + equipmentType identifies the type of equipment the seal is attached to. + + + + + extendedSealIdentifier is the identifier of the seal attached to the equipment. + + + + + + + +Generation 2: +The extended seal identifier uniquely identifies a seal (Annex IC requirement 401). + +Note: VDO uses base64 encoded strings in manufacturerCode and sealIdentifier + + + + + manufacturerCode is a code of the manufacturer of the seal. + +manufacturerCode ::= OCTET STRING(SIZE(2)) + +Note: Actual type is a string, see urn:CELEX:02016R0799-20180417:Annex I C:5.3:401 + + + + + + + + + + + sealIdentifier is an identifier for the seal which is unique for the manufacturer. + +sealIdentifier ::= OCTET STRING(SIZE(8)) + +Note: Actual type is an alphanumeric string, see urn:CELEX:02016R0799-20180417:Annex I C:5.3:401 + + + + + + + + + + + + + +Generation 2: +Information, stored in a vehicle unit, about a tachograph card used (Annex IC requirement 132). + + + + + rememberCardType=1 + cardNumberAndGenerationInformation is the full card number and generation of the card used (data type 2.74). + + + + + cardExtendedSerialNumber as read from the file EF_ICC under the MF of the card. + + + + + cardStructureVersion as read from the file EF_Application_Identification under the DF_Tachograph_G2. + + + + + cardNumber as read from the file EF_Identification under the DF_Tachograph_G2. + + + + + + + +Generation 2: +Information stored in a vehicle unit, related to the consent of a driver to use Intelligent Transport Systems. + + + + + cardNumberAndGen identifies the card including its generation. This must be a driver card or a workshop card. + + + + + consent is a flag which indicates whether the driver has given his consent on the usage of Intelligent Transport Systems with this vehicle / vehicle unit. + +Value assignment: +TRUE indicates the driver's consent to use Intelligent Transport Systems +FALSE indicates the driver's denial to use Intelligent Transport Systems + + + + + + + +Information, stored in a card, related to the identification of the integrated circuit (IC) card (Annex 1C requirement 248). + + + + + clockStop is the Clockstop mode as defined in EN 726-3. + +clockStop ::= OCTET STRING(SIZE(1)) + + + + + cardExtendedSerialNumber is the IC card serial number and IC card manufacturing reference as defined in EN 726-3 and as further specified by the ExtendedSerialNumber data type. + + + + + cardApprovalNumber is the type approval number of the card. + + + + + cardPersonaliserID is the card personaliser ID as defined in EN 726-3. + + + + + embedderIcAssemblerId is the embedder/IC assembler identifier as defined in EN 726-3. + + + + + + + + + + icIdentifier is the Identifier of the IC on the card and its IC manufacturer as defined in EN 726-3. + +icIdentifier ::= OCTET STRING(SIZE(2)) + + + + + + + +Information, stored in a card, related to the identification of the card's Integrated Circuit (IC) (Annex 1C requirement 249). +The icSerialNumber together with the icManufacturingReferences identifies the card chip uniquely. +The icSerialNumber alone does not uniquely identify the card chip. + + + + + icSerialNumber is the IC serial number as defined in EN 726-3. + + + + + icManufacturingReferences is the IC manufacturer identifier and fabrication elements as defined in EN 726-3. + + + + + + + +Information, stored in a driver card related to the identification of the application of the card (Annex 1C requirement 253 and 278). + + + + + typeOfTachographCardId is specifying the implemented type of card. + + + + + cardStructureVersion is specifying the version of the structure that is implemented in the card. + + + + + noOfEventsPerType is the number of events per type of event the card can record. + + + + + + + + + + + noOfFaultsPerType is the number of faults per type of fault the card can record. + + + + + + + + + + + activityStructureLength indicates the number of bytes available for storing activity records. + + + + + + + + + + + noOfCardVehicleRecords is the number of vehicle records the card can contain. + + + + + + + + + + + noOfCardPlaceRecords is the number of places the card can record. + + + + + + + + + + + generation=2.. + noOfGNSSADRecords is the number of GNSS accumulated driving records the card can store. + + + + + + + + + + + generation=2.. + noOfSpecificConditionRecords is the number of specific condition records the card can store. + + + + + + + + + + + generation=2.. + noOfCardVehicleUnitRecords is the number of vehicle units used records the card can store. + + + + + + + + + + + + + +Generation 2, version 2: +Information, stored in a driver card related to the identification of the application of the card (Annex IC requirement 278a). + + + + + lengthOfFollowingData is the number of bytes following in the record. + + + + + noOfBorderCrossingRecords is the number of border crossing records the driver card can store. + + + + + + + + + + + noOfLoadUnloadRecords is the number of load/unload records the driver card can store. + + + + + + + + + + + noOfLoadTypeEntryRecords is the number of load type entry records the driver card can store. + + + + + + + + + + + vuConfigurationLengthRange is the number of bytes in a tachograph card, available to store VU configurations. + + + + + + + + + + + + + +Information, stored in a workshop card related to the identification of the application of the card (Annex 1C requirement 307 and 330). + + + + + typeOfTachographCardId is specifying the implemented type of card. + + + + + cardStructureVersion is specifying the version of the structure that is implemented in the card. + + + + + noOfEventsPerType is the number of events per type of event the card can record. + + + + + + + + + + + noOfFaultsPerType is the number of faults per type of fault the card can record. + + + + + + + + + + + activityStructureLength indicates the number of bytes available for storing activity records. + + + + + + + + + + + noOfCardVehicleRecords is the number of vehicle records the card can contain. + + + + + + + + + + + noOfCardPlaceRecords is the number of places the card can record. + + + + + + + + + + + noOfCalibrationRecords is the number of calibration records the card can store. + + + + + + + + + + + generation=2.. + noOfGNSSADRecords is the number of GNSS accumulated driving records the card can store. + + + + + + + + + + + generation=2.. + noOfSpecificConditionRecords is the number of specific condition records the card can store. + + + + + + + + + + + generation=2.. + noOfCardVehicleUnitRecords is the number of vehicle units used records the card can store. + + + + + + + + + + + + + +Generation 2, version 2: +Information, stored in a workshop card related to the identification of the application of the card (Annex IC requirement 330a). + + + + + lengthOfFollowingData is the number of bytes following in the record. + + + + + noOfBorderCrossingRecords is the number of border crossing records the workshop card can store. + + + + + + + + + + + noOfLoadUnloadRecords is the number of load/unload records the workshop card can store. + + + + + + + + + + + noOfLoadTypeEntryRecords is the number of load type entry records the workshop card can store. + + + + + + + + + + + vuConfigurationLengthRange is the number of bytes in a tachograph card, available to store VU configurations. + + + + + + + + + + + + + +Code indicating the version of the implemented structure in a tachograph card. + +CardStructureVersion ::= OCTET STRING(SIZE(2)) + + + + + Index for changes of the structure. + +‘00’H for Generation 1 applications +‘01’H for Generation 2 applications + + + + + Index for changes concerning the use of the data elements defined for the structure given by the high byte. + +‘00’H for this version of Generation 1 applications +‘00’H for this version of Generation 2 applications + + + + + + + +Information, stored in a card, related to the identification of the card (Annex 1C requirements 255, 280, 310, 333, 359, 365, 371, and 377). + + + + + cardIssuingMemberState is the code of the Member State issuing the card. + + + + + cardNumber is the card number of the card. + + + + + cardIssuingAuthorityName is the name of the authority having issued the Card. + + + + + cardIssueDate is the issue date of the Card to the current holder. + + + + + cardValidityBegin is the first date of validity of the card. + + + + + cardExpiryDate is the date when the validity of the card ends. + + + + + + + +Information, stored in a driver card, related to the identification of the cardholder (Annex 1C requirement 256 and 281). + + + + + cardHolderName is the name and first name(s) of the holder of the Driver Card. + + + + + cardHolderBirthDate is the date of birth of the holder of the Driver Card. + + + + + cardHolderPreferredLanguage is the preferred language of the card holder. + + + + + + + +Date expressed in a readily printable numeric format. + +‘00000000’H denotes explicitly no date. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Information, stored in a driver card, related to the card holder driver licence data (Annex 1C requirement 259 and 284). + + + + + drivingLicenceIssuingAuthority is the authority responsible for issuing the driving licence. + + + + + drivingLicenceIssuingNation is the nationality of the authority that issued the driving licence. + + + + + drivingLicenceNumber is the number of the driving licence. + + + + + + + + + + + + + +Information, stored in a driver or a workshop card, related to an event associated to the card holder (Annex 1C requirements 261, 286, 318 and 341). + + + + + eventType is the type of the event. + + + + + eventBeginTime is the date and time of beginning of event. + + + + + eventEndTime is the date and time of end of event. + + + + + eventVehicleRegistration is the VRN and registering Member State of vehicle in which the event happened. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CardEventData is a sequence, ordered by ascending value of EventFaultType, of cardEventRecords (except security breach attempts related records which are gathered in the last set of the sequence). + +Generation 1: +Information, stored in a driver or workshop card, related to the events associated with the card holder (Annex IC requirements 260 and 318). +SEQUENCE SIZE(6) + +Generation 2: +Information, stored in a driver or workshop card, related to the events associated with the card holder (Annex IC requirements 285 and 341). +SEQUENCE SIZE(11) + + + + + cardEventRecords is a set of event records of a given event type (or category for security breach attempts events). + + + + + + + +Information, stored in a driver or a workshop card, related to a fault associated to the card holder (Annex 1C requirement 264, 289, 318, and 341). + + + + + faultType is the type of the fault. + + + + + faultBeginTime is the date and time of beginning of fault. + + + + + faultEndTime is the date and time of end of fault. + + + + + faultVehicleRegistration is the VRN and registering Member State of vehicle in which the fault happened. + + + + + + + +CardFaultData is a sequence of Recording Equipment faults set of records followed by card faults set of records. + +Information, stored in a driver or a workshop card, related to the faults associated to the card holder (Annex 1C requirements 263, 288, 318, and 341). +SEQUENCE SIZE(2) + + + + + cardFaultRecords is a set of fault records of a given fault category (Recording Equipment or card). + + + + + + + +Information, stored in a driver or a workshop card, related to the activities of the driver (Annex 1C requirements 267, 268, 292, 293, 321 and 344). + + + + + activityPointerOldestDayRecord is the specification of the begin of the storage place (number of bytes from the beginning of the string) of the oldest complete day record in the activityDailyRecords string. The maximum value is given by the length of the string. + +activityPointerOldestDayRecord ::= INTEGER(0..CardActivityLengthRange-1) + + + + + activityPointerNewestRecord is the specification of the begin of the storage place (number of bytes from the beginning of the string) of the most recent day record in the activityDailyRecords string. The maximum value is given by the length of the string. + +activityPointerNewestRecord ::= INTEGER(0..CardActivityLengthRange-1) + + + + + activityDailyRecords is the space available to store the driver activity data (data structure: CardActivityDailyRecord) for each calendar day where the card has been used. + + + + + + + + + + + + + +Information, stored in a driver or a workshop card, related to the activities of the driver (Annex 1C requirements 267, 268, 292, 293, 321 and 344). + + + + + activityPointerOldestDayRecord is the specification of the begin of the storage place (number of bytes from the beginning of the string) of the oldest complete day record in the activityDailyRecords string. The maximum value is given by the length of the string. + +activityPointerOldestDayRecord ::= INTEGER(0..CardActivityLengthRange-1) + + + + + activityPointerNewestRecord is the specification of the begin of the storage place (number of bytes from the beginning of the string) of the most recent day record in the activityDailyRecords string. The maximum value is given by the length of the string. + +activityPointerNewestRecord ::= INTEGER(0..CardActivityLengthRange-1) + + + + + activityDailyRecords is the space available to store the driver activity data (data structure: CardActivityDailyRecord) for each calendar day where the card has been used. + + + + + + + + +Information, stored in a driver or workshop card, related to the vehicles used by the card holder (Annex 1C requirements 270, 295, 323, and 346). + + + + + vehiclePointerNewestRecord is the index of the last updated vehicle record. + +vehiclePointerNewestRecord ::= INTEGER(0..NoOfCardVehicleRecords-1) + +Value assignment: Number corresponding to the numerator of the vehicle record, beginning with ‘0’ for the first occurrence of the vehicle records in the structure. + + + + + cardVehicleRecords is the set of records containing information on vehicles used. + + + + + + + +Information, stored in a driver or workshop card, related to a period of use of a vehicle during a calendar day (Annex 1C requirements 269, 294, 322, and 345). + +Generation 2: +In addition to generation 1 the following data element is used: VehicleIdentificationNumber. + + + + + vehicleOdometerBegin is the vehicle odometer value at the beginning of the period of use of the vehicle. + + + + + vehicleOdometerEnd is the vehicle odometer value at the end of the period of use of the vehicle. + + + + + vehicleFirstUse is the date and time of the beginning of the period of use of the vehicle. + + + + + vehicleLastUse is the date and time of the end of the period of use of the vehicle. + + + + + vehicleRegistration is the VRN and the registering Member State of the vehicle. + + + + + vuDataBlockCounter is the value of the VUDataBlockCounter at last extraction of the period of use of the vehicle. + + + + + generation=2.. + vehicleIdentificationNumber is the vehicle identification number referring to the vehicle as a whole. + + + + + + + +Information, stored in a driver or a workshop card, related to the places where daily work periods begin and/or end (Annex 1C requirements 272, 297, 325, and 348). + + + + + placePointerNewestRecord is the index of the last updated place record. + +placePointerNewestRecord ::= INTEGER(0..NoOfCardPalceRecords-1) + +Value assignment: Number corresponding to the numerator of the place record, beginning with ‘0’ for the first occurrence of the place records in the structure. + + + + + generation=1;base=int8 + + + + + + + placeRecords is the set of records containing the information related to the places entered. + + + + + + + +Information about the actual usage of the card (Annex 1C requirement 273, 298, 326, and 349). + + + + + sessionOpenTime is the time when the card is inserted for the current usage. This element is set to zero at card removal. + + + + + sessionOpenVehicle is the identification of the currently used vehicle, set at card insertion. This element is set to zero at card removal. + + + + + + + +Information, stored in a driver card, a workshop card or a vehicle unit, related to a specific condition (Annex 1C requirement 131, 277, 302, 329, and 356). + + + + + generation=2.. + conditionPointerNewestRecord is the index of the last updated specific condition record. + +conditionPointerNewestRecord ::= INTEGER(0..NoOfSpecificConditionRecords-1) + +Value assignment: Number corresponding to the numerator of the specific condition record, beginning with ‘0’ for the first occurrence of the specific condition record in the structure. + + + + + specificConditionRecords is the set of records containing information on the specific conditions recorded. + + + + + + + +Information, stored in a driver or workshop card, related to the vehicle units used by the card holder (Annex 1C requirement 306 and 352). + + + + + vehicleUnitPointerNewestRecord is the index of the last updated vehicle unit record. + +vehicleUnitPointerNewestRecord ::= INTEGER(0..NoOfCardVehicleUnitRecords-1) + +Value assignment: Number corresponding to the numerator of the vehicle unit record, beginning with ‘0’ for the first occurrence of the vehicle unit records in the structure. + + + + + cardVehicleUnitRecords is the set of records containing information on vehicle units used. + + + + + + + +Generation 2: +Information, stored in a driver or workshop card, related to a vehicle unit that was used (Annex 1C requirement 303 and 351). + + + + + entryTime is the date and time of the entry. + + + + + manufacturerCode identifies the manufacturer of the Vehicle Unit. + + + + + deviceID identifies the Vehicle Unit type of a manufacturer. The value is manufacturer specific. + +deviceID ::= INTEGER(0..255) + + + + + vuSoftwareVersion is the software version number of the Vehicle Unit. + + + + + + + +Generation 2: +Information, stored in a driver or workshop card, related to the GNSS position of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirement 306 and 354). + + + + + gnssADPointerNewestRecord is the index of the last updated GNSS accumulated driving record. + +gnssADPointerNewestRecord ::= INTEGER(0..NoOfGnssADRecords-1) + +Value assignment is the number corresponding to the numerator of the GNSS accumulated driving record, beginning with '0' for the first occurrence of the GNSS accumulated driving record in the structure. + + + + + gnssAccumulatedDrivingRecords is the set of records containing the date and time the accumulated driving reaches a multiple of three hours and information on the position of the vehicle. + + + + + + + +Generation 2: +Information, stored in a driver or workshop card, related to the GNSS position of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirement 305 and 353). + + + + + timeStamp is the date and time when the accumulated driving time reaches a multiple of three hours. + + + + + gnssPlaceRecord contains information related to the position of the vehicle. + + + + + vehicleOdometerValue is the odometer value when the accumulated driving time reaches a multiple of three hours. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or a workshop card, providing the authentication status of places where daily work periods begin and/or end (Annex IC requirements 306b and 356b). + + + + + placeAuthPointerNewestRecord is the index of the last updated place authentication status record. + +placeAuthPointerNewestRecord ::= INTEGER(0..NoOfCardPlaceRecords-1) + +Value assignment: Number corresponding to the numerator of the place authentication status record, beginning with ‘0’ for the first occurrence of the place authentication status records in the structure. + + + + + placeAuthStatusRecords is the set of records containing the place authentication status of the places entered. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, providing the authentication status of a place where a daily work period begins or ends (Annex IC requirements 306a and 356a). Other information related to the place itself is stored in another record (see 2.117 PlaceRecord). + + + + + entryTime is a date and time related to the entry (which is the same date and time as in the corresponding PlaceRecord). + + + + + authenticationStatus is the authentication status of the recorded GNSS position. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, providing the authentication status of GNSS positions of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirements 306d and 356d). + + + + + gnssAuthADPointerNewestRecord is the index of the last updated GNSS position authentication status record. + +gnssAuthADPointerNewestRecord ::= INTEGER(0..NoOfGnssADRecords-1) + +Value assignment is the number corresponding to the numerator of the GNSS position authentication status record, beginning with '0' for the first occurrence of the GNSS position authentication status record in the structure. + + + + + gnssAuthStatusADRecords is the set of records containing the date and time the accumulated driving reaches a multiple of three hours and the authentication status of the GNSS position. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, providing the authentication status of a GNSS position of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirements 306c and 356c). Other information related to the GNSS position itself is stored in another record (see 2.79 GNSSAccumulatedDrivingRecord). + + + + + timeStamp is the date and time when the accumulated driving time reaches a multiple of three hours (which is the same date and time as in the corresponding GNSSAccumulatedDrivingRecord). + + + + + authenticationStatus is the authentication status of the GNSS position when the accumulated driving time reaches a multiple of three hours. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, related to the border crossings of the vehicle when the latter has crossed the border of a country (Annex IC requirements 306f and 356f). + + + + + borderCrossingPointerNewestRecord is the index of the last updated card border crossing record. + +borderCrossingPointerNewestRecord ::= INTEGER(0..NoOfBorderCrossingRecords-1) + +Value assignment is the number corresponding to the numerator of the card border crossing record, beginning with ‘0’ for the first occurrence of the card border crossing record in the structure. + + + + + cardBorderCrossingRecords is the set of card border crossing records. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, related to the border crossings of the vehicle when the latter has crossed the border of a country (Annex IC requirements 147b, 306e and 356e). + + + + + countryLeft is the country which was left by the vehicle, or ‘no information available’ according to Annex IC requirement 147b. ‘Rest of the World’ (NationNumeric code ‘FF’H) shall be used when the vehicle unit is not able to determine the country where the vehicle is located (e.g. the current country is not part of the stored digital maps). + + + + + countryEntered is the country into which the vehicle has entered, or the country in which the vehicle is located at card insertion time. ‘Rest of the World’ (NationNumeric code ‘FF’H) shall be used when the vehicle unit is not able to determine the country where the vehicle is located (e.g. the current country is not part of the stored digital maps). + + + + + gnssPlaceAuthRecord contains information related to the position of the vehicle, when the vehicle unit has detected that the vehicle has crossed the border of a country, or ‘no information available’ according to requirement 147b of Annex IC, and its authentication status. + + + + + vehicleOdometerValue is the odometer value when the vehicle unit has detected that the vehicle has crossed the border of a country, or ‘no information available’ according to requirement 147b of Annex IC. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, related to load/unload operations of the vehicle (Annex IC requirements 306h and 356h). + + + + + +loadUnloadPointerNewestRecord is the index of the last updated card load/unload record. + +loadUnloadPointerNewestRecord ::= INTEGER(0..NoOfLoadUnloadBorderCrossingRecords-1) + +Value assignment: is the number corresponding to the numerator of the card load/unload record, beginning with '0' for the first occurrence of the card load/unload record in the structure. + + + + + cardLoadUnloadRecords is the set of records containing the indication of the type of operation performed (load, unload, or simultaneous load and unload), the date and time the load/unload operation has been entered, information about the position of the vehicle, and the vehicle odometer value. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, related to load/unload operations of the vehicle (Annex IC requirements 306g and 356g). + + + + + timeStamp is the date and time at the beginning of the load/unload operation. + + + + + operationType is the type of operation entered (load, unload, or simultaneous load/unload). + + + + + gnssPlaceAuthRecord contains information related to the position of the vehicle. + + + + + vehicleOdometerValue is the odometer value related to the beginning of the load/unload operation. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, related to the load type entries when the card is inserted in a vehicle unit (Annex IC requirements 306j and 356j). + + + + + +loadTypeEntryPointerNewestRecord is the index of the last updated card load type entry record. + +loadTypeEntryPointerNewestRecord ::= INTEGER(0..NoOfLoadTypeEntryRecords-1) + +Value assignment: number corresponding to the numerator of the card load type entry record, beginning with '0' for the first occurrence of the card load type entry record in the structure. + + + + + cardLoadTypeEntryRecords is the set of records containing the date and time of the entry and the load type entered. + + + + + + + +Generation 2, version 2: +Information, stored in a driver or workshop card, related to load/unload operations of the vehicle (Annex IC requirements 306h and 356h). + + + + + timeStamp is the date and time when the load type was entered. + + + + + loadTypeEntered is the load type entered. + + + + + + + +Information, stored in a driver or workshop card, related to the last control the driver has been subject to (Annex 1C requirements 274, 299, 327, and 350). + + + + + controlType is the type of the control. + + + + + controlTime is the date and time of the control. + + + + + controlCardNumber is the FullCardNumber of the control officer having performed the control. + + + + + controlVehicleRegistration is the VRN and registering Member State of the vehicle in which the control happened. + + + + + controlDownloadPeriodBegin and controlDownloadPeriodEnd is the period downloaded, in case of downloading. + + + + + controlDownloadPeriodBegin and controlDownloadPeriodEnd is the period downloaded, in case of downloading. + + + + + + + +Code indicating the activities carried out during a control. This data type is related to Annex 1C requirements 126, 274, 299, 327, and 350. + +Generation 2: +In addition to generation 1 the following flag is used: roadside calibration checking. + + + + + Card downloaded during this control activity. + + + + + VU downloaded during this control activity. + + + + + Printing done during this control activity. + + + + + Display used during this control activity. + + + + + + + +Information, stored in a card, related to the driver activities for a particular calendar day. This data type is related to Annex 1C requirements 266, 291, 320 and 343. + + + + + activityRecordDate is the date of the record. + + + + + activityDailyPresenceCounter is the daily presence counter for the card this day. + + + + + + + + + + activityDayDistance is the total distance travelled this day. + + + + + noOfActivityChanges ::= INTEGER(1..1440) + + + + + activityChangeInfo is the set of ActivityChangeInfo data for the driver this day. It may contain at maximum 1440 values (one activity change per minute). This set always includes the activityChangeInfo coding the driver status at 00.00. + + + + + + + +Information, stored in a workshop card, related to the identification of the cardholder (Annex 1C requirement 311 and 334). + + + + + workshopName is name of the workshop of the card holder. + + + + + workshopAddress is the address of the workshop of the card holder. + + + + + cardHolderName is the name and first name(s) of the holder (e.g. the name of the mechanic). + + + + + cardHolderPreferredLanguage is the preferred language of the card holder. + + + + + + + +Information, stored in a workshop card, related to workshop activity performed with the card (Annex 1C requirements 314, 316, 337, and 339). + + + + + calibrationTotalNumber is the total number of calibrations performed with the card. + +calibrationTotalNumber ::= INTEGER(0..2^16-1) + + + + + calibrationPointerNewestRecord is the index of the last updated calibration record. + +calibrationPointerNewestRecord ::= INTEGER(0..NoOfCalibrationRecords-1) + +Value assignment: Number corresponding to the numerator of the calibration record, beginning with ‘0’ for the first occurrence of the calibration records in the structure. + + + + + generation=1;base=uint8 + + + + + + + calibrationRecords is the set of records containing calibration and/or time adjustment information. + + + + + + + +Information, stored in a workshop card, related to a calibration performed with the card (Annex 1C requirement 314 and 337). + +Generation 2: +In addition to generation 1 the following data elements are used: sensorGNSSSerialNumber, rcmSerialNumber, sealDataCard. + + + + + calibrationPurpose is the purpose of the calibration. + + + + + vehicleIdentificationNumber is the VIN. + + + + + vehicleRegistration contains the VRN and registering Member State. + + + + + wVehicleCharacteristicConstant is the characteristic coefficient of the vehicle. + + + + + kConstantOfRecordingEquipment is the constant of the recording equipment. + + + + + lTyreCircumference is the effective circumference of the wheel tyres. + + + + + tyreSize is the designation of the dimensions of the tyres mounted on the vehicle. + + + + + authorisedSpeed is the maximum authorised speed of the vehicle. + + + + + oldOdometerValue is the old value of the odometer. + + + + + newOdometerValue is the new value of the odometer. + + + + + oldTimeValue is the old value of date and time. + + + + + newTimeValue is the new value of date and time. + + + + + nextCalibrationDate is the date of the next calibration of the type specified in CalibrationPurpose to be carried out by the authorised inspection authority. + + + + + vuPartNumber, vuSerialNumber and sensorSerialNumber are the data elements for recording equipment identification. + + + + + vuPartNumber, vuSerialNumber and sensorSerialNumber are the data elements for recording equipment identification. + + + + + vuPartNumber, vuSerialNumber and sensorSerialNumber are the data elements for recording equipment identification. + + + + + generation=2.. + sensorGNSSSerialNumber which identifies an external GNSS facility. + + + + + generation=2.. + rcmSerialNumber which identifies a Remote Communication Module. + + + + + generation=2.. + ATTN: vuAbility is missing in type definition but exists in card file definition! + + + + + generation=2.. + sealDataCard gives information about the seals that are attached to different components of the vehicle. + + + + + + + +Generation 2: +Serial number of the Remote Communication Module. + + + + + + + + +Generation 2: +This data type stores information about the seals that are attached to the different components of a vehicle and is intended for storage on a card. This data type is related to Annex 1C requirement 337. + + + + + noOfSealRecords is the number of records in sealRecords. + +noOfSealRecords ::= INTEGER(1..5) + + + + + sealRecords is a set of seal records. + + + + + + + +Information, stored in a workshop card, related to the security data needed for pairing motion sensors to vehicle units (Annex 1C requirement 308 and 331). + +Generation 1: +SensorInstallationSecData ::= TdesSessionKey + +Value assignment: in accordance with ISO 16844-3. + +Generation 2: +As described in Appendix 11 a workshop card shall store up to three keys for VU Motion Sensor pairing. These keys have different key versions. + +SensorInstallationSecData ::= SEQUENCE { + kMWCKey1 KMWCKey, + kMWCKey2 OPTIONAL, + kMWCKey3 OPTIONAL +} + + + + + generation=1 + + + + + + generation=* + + + + + + + + +Generation 1: +A triple DES session key. + + + + + + + + + + + + + + + + + + + + + +Generation 2, version 2: +Information, stored in a workshop card, related to the additional data (i.e. by-default load type) entered during a calibration (Annex IC requirement 356l). + + + + + calibrationPointerNewestRecord is the index of the last updated calibration additional data record. + +calibrationPointerNewestRecord ::= INTEGER(0..NoOfCalibrationRecords-1) + +Value assignment is the number corresponding to the numerator of the calibration additional data record, beginning with '0' for the first occurrence of the calibration additional data record in the structure. + + + + + workshopCardCalibrationAddDataRecords is the set of records containing the old date and time value, the vehicle identification value and the by-default load type of the vehicle. + + + + + + + +Generation 2, version 2: +Information, stored in a workshop card, related to the by-default load type entered during a calibration (Annex IC requirement 356k). + + + + + oldTimeValue is the old value of date and time contained in the corresponding WorkshopCardCalibrationRecord. + + + + + vehicleIdentificationNumber is the vehicle identification number of the vehicle, also contained in the corresponding WorkshopCardCalibrationRecord. + + + + + byDefaultLoadType is the by-default load type of the vehicle (only present in version 2). + + + + + calibrationCountry is the country in which the calibration has been performed. + + + + + calibrationCountryTimestamp is the date time when the position used to determine this country was provided by the GNSS receiver. + + + + + + + +Information, stored in a control card related to the identification of the application of the card (Annex 1C requirement 357 and 363). + + + + + typeOfTachographCardId is specifying the implemented type of card. + + + + + cardStructureVersion is specifying the version of the structure that is implemented in the card. + + + + + noOfControlActivityRecords is the number of control activity records the card can store. + + + + + + + + + + + + + +Generation 2, version 2: +Information, stored in a control card related to the identification of the application of the card (Annex IC requirement 363a). + + + + + lengthOfFollowingData is the number of bytes following in the record. + + + + + vuConfigurationLengthRange is the number of bytes in a tachograph card, available to store VU configurations. + + + + + + + + + + + + + +Information, stored in a control card, related to the identification of the cardholder (Annex 1C requirement 360 and 366). + + + + + controlBodyName is the name of the control body of the card holder. + + + + + controlBodyAddress is the address of the control body of the card holder. + + + + + cardHolderName is the name and first name(s) of the holder of the Control Card. + + + + + cardHolderPreferredLanguage is the preferred language of the card holder. + + + + + + + +Information, stored in a control card, related to control activity performed with the card (Annex 1C requirement 361 and 367). + + + + + controlPointerNewestRecord is the index of the last updated control activity record. + +controlPointerNewestRecord ::= INTEGER(0..NoOfControlActivityRecords-1) + +Value assignment: Number corresponding to the numerator of the control activity record, beginning with "0" for the first occurrence of the control activity record in the structure. + + + + + controlActivityRecords is the set of all control activity records. + + + + + + + +Information, stored in a control card, related to control activity performed with the card (Annex 1C requirement 361 and 367). + + + + + controlType is the type of the control. + + + + + controlTime is the date and time of the control. + + + + + controlledCardNumber is the card number and the card issuing Member State of the card controlled. + + + + + controlledVehicleRegistration is the VRN and registering Member State of the vehicle in which the control happened. + + + + + controlDownloadPeriodBegin and controlDownloadPeriodEnd is the period eventually downloaded. + + + + + controlDownloadPeriodBegin and controlDownloadPeriodEnd is the period eventually downloaded. + + + + + + + +Information, stored in a company card related to the identification of the application of the card (Annex 1C requirement 369 and 375). + + + + + typeOfTachographCardId is specifying the implemented type of card. + + + + + cardStructureVersion is specifying the version of the structure that is implemented in the card. + + + + + noOfCompanyActivityRecords is the number of company activity records the card can store. + + + + + + + + + + + + + +Generation 2, version 2: +Information, stored in a company card related to the identification of the application of the card (Annex IC requirement 375a). + + + + + lengthOfFollowingData is the number of bytes following in the record. + + + + + vuConfigurationLengthRange is the number of bytes in a tachograph card, available to store VU configurations. + + + + + + + + + + + + + +Information, stored in a company card, related to the cardholder identification (Annex 1C requirement 372 and 378). + + + + + companyName is the name of the holder company. + + + + + companyAddress is the address of the holder company. + + + + + cardHolderPreferredLanguage is the preferred language of the card holder. + + + + + + + +CompanyActivityRecord is the sequence of information related to one company activity. + + + + + companyActivityType is the type of the company activity. + + + + + companyActivityTime is the date and time of the company activity. + + + + + cardNumberInformation is the card number and the card issuing Member State of the card downloaded, if any. + + + + + vehicleRegistrationInformation is the VRN and registering Member State of the vehicle downloaded or locked in or out. + + + + + downloadPeriodBegin and downloadPeriodEnd is the period downloaded from the VU, if any. + + + + + downloadPeriodBegin and downloadPeriodEnd is the period downloaded from the VU, if any. + + + + + + + +Information, stored in a company card, related to activities performed with the card (Annex 1C requirement 373 and 379). + + + + + companyPointerNewestRecord is the index of the last updated companyActivityRecord. + +companyPointerNewestRecord ::= INTEGER(0..NoOfCompanyActivityRecords-1) + +Value assignment: Number corresponding to the numerator of the company activity record, beginning with ‘0’ for the first occurrence of the company activity record in the structure. + + + + + companyActivityRecords is the set of all company activity records. + + + + + + + Unknown file block. + + + + + + + + + + +Generation 2: +A set of records. + + + + + recordType denotes the type of the record. + +recordType ::= RecordType + + + + + recordSize is the size of a record in bytes. + +recordSize ::= INTEGER(1..65535) + + + + + noOfRecords is the number of records in the set records. + +noOfRecords ::= INTEGER(0..65535) + + + + + + + + + + + records is the set of entries of type specified by recordType. + +records ::= SET SIZE(noOfRecords) OF [recordType] + + + + + + + + + +Generation 2, version 2: +Code indicating the version of the download interface of a vehicle unit. + +DownloadInterfaceVersion ::= OCTET STRING(SIZE(2)) + + + + + +Value assignment: ‘aabb’H: +‘aa’H + ‘00’H: not used, + ‘01’H: Generation 2 vehicle unit, + +‘bb’H + ‘00’H: not used, + ‘01’H: version 2 of Generation 2 vehicle unit. + + + + + + + + + + + + +Counter, stored in a card, identifying sequentially the insertion withdrawal cycles of the card in vehicle units. + +VuDataBlockCounter ::= BCDString(SIZE(2)) + +Value assignment: Consecutive Number with max, value 9 999, starting again with 0. + + + + + + + + + +Code identifying a language. + +Language ::= IA5String(SIZE(2)) + +Value assignment: Two-letter lower-case coding according to ISO 639. + + + + + + + + + +Type approval number of the card. + +CardApprovalNumber ::= IA5String(SIZE(8)) + +Value assignment: +The approval number shall be provided as published on the corresponding European Commission web site, i.e. for example including hyphens if any. The approval number shall be left-aligned. + + + + + + + + +Designation of tyre dimensions. + +TyreSize ::= IA5String(SIZE(15)) + +Value assignment: in accordance with Directive 92/23 (EEC) 31/03/92 O.J. L129 p.95. + + + + + + + + + +Effective circumference of the wheel tyres (definition u)). + +L-TyreCircumference ::= INTEGER(0..2^16-1) + +Value assignment: Unsigned binary, value in 1/8 mm in the operating range 0 to 8 031 mm. + + + + + + +Constant of the recording equipment (definition m)). + +K-ConstantOfRecordingEquipment ::= INTEGER(0..2^16-1) + +Value assignment: Pulses per kilometer in the operating range 0 to 64 255 pulses/km. + + + + + + +Code explaining why a set of calibration parameters was recorded. This data type is related to Annex 1B requirements 097 and 098 and Annex 1C requirements 119. + +CalibrationPurpose ::= OCTET STRING(SIZE(1)) + +Generation 2: +In addition to generation 1 the following values are used: 0x05, 0x06. + + + + + + +W-VehicleCharacteristicConstant +Characteristic coefficient of the vehicle (definition k)). + +W-VehicleCharacteristicConstant ::= INTEGER(0..2^16-1) + +Value assignment: Impulses per kilometer in the operating range 0 to 64 255 pulses/km. + + + + + + +Type approval number of the sensor. + +Generation 1: +SensorApprovalNumber ::= IA5String(SIZE(8)) + +Value assignment: Unspecified. + +Generation 2: +SensorApprovalNumber ::= IA5String(SIZE(16)) + +Value assignment: +The approval number shall be provided as published on the corresponding European Commission web site, i.e. for example including hyphens if any. The approval number shall be left-aligned. + + + + + + generation=1,*;value=8,16 + + + + + + + +Generation 2: +Type approval number of the external GNSS facility. + +SensorExternalGNSSApprovalNumber ::= IA5String(SIZE(16)) + +Value assignment: +The approval number shall be provided as published on the corresponding European Commission web site, i.e. for example including hyphens if any. The approval number shall be left-aligned. + + + + + + + + + +Code identifying a manufacturer of type approved equipment. + +ManufacturerCode ::= INTEGER(0..255) + +The laboratory competent for interoperability tests maintains and publishes the list of manufacturer codes on its web site (Annex 1C requirement 454). +ManufacturerCodes are provisionally assigned to developers of tachograph equipment on application to the laboratory competent for interoperability tests. + + + + + + +Type approval number of the vehicle unit. + +Generation 1: +VuApprovalNumber ::= IA5String(SIZE(8)) + +Value assignment: Unspecified. + +Generation 2: +VuApprovalNumber ::= IA5String(SIZE(16)) + +Value assignment: +The approval number shall be provided as published on the corresponding European Commission web site, i.e. for example including hyphens if any. The approval number shall be left-aligned. + + + + + + generation=1,*;value=8,16 + + + + + + + +Software version number of the vehicle unit. + +VuSoftwareVersion ::= IA5String(SIZE(4)) + + + + + + + + + +Part number of the vehicle unit. + +VuPartNumber ::= IA5String(SIZE(16)) + +Value assignment: VU manufacturer specific. + + + + + + + + + +Date of manufacture of the vehicle unit. + +Value assignment: Unspecified. + + + + + + +BCDString is applied for Binary Code Decimal (BCD) representation. This data type is used to represent one decimal digit in one semi octet (4 bits). BCDString is based on the ISO/IEC 8824-1 ‘CharacterStringType’. + +BCDString uses an ‘hstring’ notation. The leftmost hexadecimal digit shall be the most significant semi octet of the first octet. To produce a multiple of octets, zero trailing semi octets shall be inserted, as needed, from the leftmost semi octet position in the first octet. + +Permitted digits are: 0, 1, .. 9. + + + + + + +Number of over speeding events since the last over speeding control. + +OverspeedNumber ::= INTEGER(0..255) + +Value assignment: 0 means that no over speeding event has occurred since the last over speeding control, 1 means that one over speeding event has occurred since the last over speeding control …255 means that 255 or more over speeding events have occurred since the last over speeding control. + + + + + + +The number of similar events for one given day (Annex 1B requirement 094 and Annex 1C requirement 117). + +SimilarEventsNumber ::= INTEGER(0..255) + +Value assignment: 0 is not used, 1 means that only one event of that type has occurred and has been stored on that day, 2 means that 2 events of that type has occurred on that day (one only has been stored), …255 means that 255 or more events of that type have occurred on that day. + + + + + + +Code explaining why an event or a fault has been recorded. + +EventFaultRecordPurpose ::= OCTET STRING(SIZE(1)) + + + + + + +Code qualifying an event or a fault. + +EventFaultType ::= OCTET STRING(SIZE(1)) + + + + + + +Code identifying a specific condition (Annex 1B requirements 050b, 105a, 212a and 230a and Annex 1C requirements 62). + +SpecificConditionType ::= INTEGER(0..255) + + + + + + +Generation 2, version 2. + +PositionAuthenticationStatus ::= INTEGER(0..255) + + + + + + + +Generation 2, version 2: +Code identifying a load type entered. + +LoadType ::= INTEGER(0..255) + + + + + + + +Generation 2, version 2: +Code identifying a type of operation entered. + +OperationType ::= INTEGER(0..255) + + + + + + + +Numerical reference to a region within a specified country. + +RegionNumeric ::= OCTET STRING(SIZE(1)) + +Generation 2: +The RegionNumeric codes shall be held on a list maintained on the website of the laboratory appointed to carry out interoperability testing. + + + + + + +Code to distinguish between begin and end for an entry of a daily work period place and condition of the entry. + +Generation 1: +EntryTypeDailyWorkPeriod ::= INTEGER { + Begin, related time = card insertion time or time of entry (0), + End, related time = card withdrawal time or time of entry (1), + Begin, related time manually entered (string time) (2), + End, related time manually entered (end of work period) (3), + Begin, related time assumed by VU (4), + End, related time assumed by VU (5) +} + +Value assignment: according to ISO/IEC8824-1. + +Generation 2: +EntryTypeDailyWorkPeriod ::= INTEGER { + Begin, related time = card insertion time or time of entry (0), + End, related time = card withdrawal time or time of entry (1), + Begin, related time manually entered (string time) (2), + End, related time manually entered (end of work period) (3), + Begin, related time assumed by VU (4), + End, related time assumed by VU (5), + Begin, related time based on GNSS data (6), + End, related time based on GNSS data (7) +} + +Value assignment: according to ISO/IEC8824-1. + + + + + + +This data type enables to code, within a two bytes word, a slot status at 00:00 and/or a driver status at 00:00 and/or changes of activity and/or changes of driving status and/or changes of card status for a driver or a co-driver. This data type is related to Annex 1C requirements 105, 266, 291, 320, 321, 343, and 344. + +ActivityChangeInfo ::= OCTET STRING(SIZE(2)) + + + + + + +Code identifying whether a cardholder has manually entered driver activities at card insertion or not (Annex 1B requirement 081 and Annex 1C requirement 102). + +ManualInputFlag ::= INTEGER { + noEntry (0), + manualEntries (1) +} + + + + + + +Code to distinguish between the two slots of a Vehicle Unit. + +CardSlotNumber ::= INTEGER { + driverSlot (0), + co-driverSlot (1) +} + + + + + + + + + +Vehicle Identification Number (VIN) referring to the vehicle as a whole, normally chassis serial number or frame number. + +VehicleIdentificationNumber ::= IA5String(SIZE(17)) + +Value assignment: As defined in ISO 3779. + + + + + + + + + +Alphabetic reference to a country shall be in accordance with the distinguishing signs used on vehicles in international traffic (United Nations Vienna Convention on Road Traffic, 1968). + +NationAlpha ::= IA5String(SIZE(3)) + +The Nation Alpha and Numeric codes shall be held on a list maintained on the website of the laboratory appointed to carry out interoperability testing, as set out in Annex 1C requirement 440. + + + + + + + + + +Numerical reference to a country. + +NationNumeric ::= INTEGER(0..255) + +Value assignment: see data type 2.100 (NationAlpha). + +Any amendment or updating of the Nation Alpha or Numeric specification described in the above paragraph shall only be made out after the appointed laboratory has obtained the views of type approved digital and smart tachograph vehicle unit manufacturers. + + + + + + + + + + + + +A digital signature. + +Generation 1: +Signature ::= OCTET STRING(SIZE(128)) + +Value assignment: in accordance with Appendix 11 Common security mechanisms. + +Generation 2: +Signature ::= OCTET STRING(SIZE(64..132)) + +Value assignment: in accordance with Appendix 11 Common security mechanisms. + + + + + generation=1,*;value=128, + + + + + + + + + + +Code for a combined date and time field, where the date and time are expressed as seconds past 00h.00m.00s. on 1 January 1970 UTC. + +TimeReal ::= INTEGER(0..TimeRealRange) +TimeRealRange ::= 2^32-1 + +Value assignment – Octet aligned: Number of seconds since midnight 1 January 1970 UTC. +The max. possible date/time is in the year 2106. + + + + + + + + + INTEGER(-128..127) + + + + + + + + + INTEGER(-(2^15)..2^15-1) + + + + + + + + + INTEGER(-(2^23)..2^23-1) + + + + + + + + + INTEGER(-(2^31)..2^31-1) + + + + + + + + + INTEGER(0..255) + + + + + + + + + INTEGER(0..2^16-1) + + + + + + + + + INTEGER(0..2^24-1) + + + + + + + + + INTEGER(0..2^32-1) + + + + + + + + + +A card renewal index (definition i)). + +CardRenewalIndex ::= IA5String(SIZE(1)). + +Value assignment: (see this Annex chapter 7). +‘0’ First issue. +Order for increase: ‘0, …, 9, A, …, Z’ + + + + + + + + + +A card replacement index (definition j)). + +CardReplacementIndex ::= IA5String(SIZE(1)) + +Value assignment: (see this Annex chapter 7). +‘0’ Original card. +Order for increase: ‘0, …, 9, A, …, Z’ + + + + + + + + + +A card consecutive index (definition h)). + +CardConsecutiveIndex ::= IA5String(SIZE(1)) + +Value assignment: (see Annex 1C chapter 7) +Order for increase: ‘0, …, 9, A, …, Z, a, …, z’ + + + + + + + + + +A distance travelled (result of the calculation of the difference between two vehicle's odometer values in kilometers). + +Distance ::= INTEGER(0..2^16-1) + +Value assignment: Unsigned binary. Value in km in the operational range 0 to 9 999 km. + + + + + + +Odometer value of the vehicle in a short form. + +OdometerShort ::= INTEGER(0..2^24-1) + +Value assignment: Unsigned binary. Value in km in the operating range 0 to 9 999 999 km. + + + + + + +Speed of the vehicle (km/h). + +Speed ::= INTEGER(0..255) + +Value assignment: kilometers per hour in the operational range 0 to 220 km/h. + + + + + + +Code to distinguish different types of equipment for the tachograph application. + +EquipmentType ::= INTEGER(0..255) + +Value assignment: According to ISO/IEC8824-1. + +Value 0 is reserved for the purpose of designating a Member State or Europe in the CHA field of certificates. + + + + + + +The current date and time of the recording equipment. + + + + + + +Code indicating the type of cards inserted in the two slots of the vehicle unit. + +CardSlotsStatus ::= OCTET STRING(SIZE(1)) + + + + + + +Generation 2: +The vehicle's odometer value at midnight on a given day (Annex 1B requirement 090 and Annex 1C requirement 113). + + + + + + +Date of installation of the vehicle unit software version. + + + + + + +Date of a pairing of the motion sensor with a vehicle unit. + + + + + + +Generation 2: +Reference to a record type. This data type is used in RecordArrays. + +RecordType ::= OCTET STRING(SIZE(1)) + + + + + + +Generation 2: +Indicates the generation of tachograph used. + +Generation ::= INTEGER(0..255) + + + + + + +Indicates the generation and version of tachograph used. + + + + + + +Generation 2: +The date and time of the download. + + + + + + +Generation 2: +The accuracy of the GNSS position data (definition eee)). This accuracy is encoded as integer and is a multiple (factor 10) of the X.Y value provided by the GSA NMEA sentence. + +GnssAccuracy ::= INTEGER(1..100) + > +‘GNSS accuracy’: +in the context of recording the position from a Global Navigation Satellite System (GNSS) with tachographs, means the value of the horizontal dilution of precision (HDOP) calculated as the minimum of the HDOP values collected on the available GNSS systems; + + + + base=uint8;factor=10 + + + + + + + + + + +Generation 2: +Information stored in a VU on the ability of the VU to use generation 1 tachograph cards or not (Annex 1C requirement 121). + +VuAbility ::= OCTET STRING(SIZE(1)) + +Value assignment: Octet Aligned:‘xxxxxxxa’B (8 bits) + +‘a’B Ability to support generation 1 tachograph cards: + ‘0’B Generation 1 is supported, + ‘1’B Generation1 is not supported, + +‘xxxxxxx’B RFU + + + + + + +Generation 2, version 2: +Version of the digital map stored in the vehicle unit (Annex IC requirement 133j). + +VuDigitalMapVersion ::= IA5String(SIZE(12)) + +Value assignment: as specified on the dedicated secured website made available by the European Commission (Annex IC requirement 133k). + + + + + + + + + +Generation 2: +Date of a coupling of the external GNSS facility with a vehicle unit. + + + + + + +Code indicating an activity carried out by a company using its company card. + +CompanyActivityType ::= INTEGER { + card downloading (1), + VU downloading (2), + VU lock-in (3), + VU lock-out (4) +} + + + + + + +Number of events per type of event a card can store. + +NoOfEventsPerType ::= INTEGER(0..255) + + + + + + +Number of faults per type of fault a card can store. + +NoOfFaultsPerType ::= INTEGER(0..255) + + + + + + +Number of bytes in a driver or a workshop card, available to store driver activity records. + +CardActivityLengthRange ::= INTEGER(0..2^16-1) + + + + + + +Number of vehicles used records a driver or workshop card can store. + +NoOfCardVehicleRecords ::= INTEGER(0..2^16-1) + + + + + + +Number of place records a driver or workshop card can store. + +Generation 1: +NoOfCardPlaceRecords ::= INTEGER(0..255) + +Generation 2: +NoOfCardPlaceRecords ::= INTEGER(0..2^16-1) + + + + generation=1;base=uint8 + + + + + + +Generation 2: +Number of GNSS accumulated driving records a card can store. + +NoOfGnssADRecords ::= INTEGER(0..2^16-1) + + + + + + +Generation 2: +Number of specific condition records a card can store. + +NoOfSpecificConditionRecords ::= INTEGER(0..2^16-1) + + + + + + +Generation 2: +Number of vehicle units used records a driver or workshop card can store. + +NoOfCardVehicleUnitRecords ::= INTEGER(0..2^16-1) + + + + + + +Generation 2, version 2: +Length indicator for extensible records. + +LengthOfFollowingData ::= INTEGER(0..2^16-1) + + + + + + +Generation 2, version 2: +Number of border crossing records a driver or workshop card can store. + +NoOfBorderCrossingRecords ::= INTEGER(0..2^16-1) + + + + + + +Generation 2, version 2: +Number of load/unload records a card can store. + +NoOfLoadUnloadRecords ::= INTEGER(0..2^16-1) + + + + + + +Generation 2, version 2: +Number of load type entry records a driver or workshop card can store. + +NoOfLoadTypeEntryRecords ::= INTEGER(0..2^16-1) + + + + + + +Generation 2, version 2: +Number of bytes in a tachograph card, available to store VU configurations. + +VUConfigurationLengthRange ::= INTEGER(0..2^16-1) + + + + + + +Number of control activity records, a control card can store. + +NoOfControlActivityRecords ::= INTEGER(0..2^16-1) + + + + + + +Number of calibration records, a workshop card can store. + +Generation 1: +NoOfCalibrationRecords ::= INTEGER(0..255) + +Generation 2: +NoOfCalibrationRecords ::= INTEGER(0..2^16-1) + + + + generation=1;base=uint8 + + + + + + +Counter indicating the number of calibrations performed with a workshop card since its last download (Annex 1C requirement 317 and 340). + +NoOfCalibrationsSinceDownload ::= INTEGER(0..2^16-1) + + + + + + +Number of company activity records, a company card can store. + +NoOfCompanyActivityRecords ::= INTEGER(0..2^16-1) + + + + + + +Latitude or longitude value either in encoded format (±DDMM.M) or in degrees after being processed by FKXMLtoDB. + + + + base=int24 + + + + + + +EF VU_Configuration shall contain the cardholder tachograph specific settings. + + + + + + +Vehicle unit + + + + + + + id=0x7600 + Positive Response Transfer Data Download Interface Version + + + + + + +ATTN: Following documentation contradicts documentation of DownloadInterfaceVersion structure! + +Generation and version of the VU: 02,02 Hex for Generation 2, version 2. +Not supported by Generation 1 and Generation 2, version 1 VU, which shall respond negatively (Sub function not supported, see DDP_018) + + + + + + + + id=0x7601,0x7621,0x7631;generation=1,2,3 + Positive Response Transfer Data Overview + + + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The member state certificate plus metadata as used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The VU certificate plus metadata as used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The Vehicle Identification Number plus metadata as used in the download protocol. + + + + + generation=1,2,*;isRecordArray=0,1;recordName=,vehicleRegistrationNumber, + +Generation 2, version 1: +The Vehicle Registration Number plus metadata as used in the download protocol. + +Generation 2, version 2: +The Vehicle Registration Identification plus metadata as used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The current date and time plus metadata as used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The VUDownloadablePeriod plus metadata used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The CardSlotsStatus plus metadata as used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +Information related to the last VU download (Annex 1C requirement 129). + + + + + generation=1,*;isRecordArray=0,1;recordName=vuCompanyLocksRecords + +Generation 2: +Information, stored in a vehicle unit, related to company locks (Annex 1C requirement 128). + + + + + generation=1,*;isRecordArray=0,1;recordName=vuControlActivityRecords + +Generation 2: +Information, stored in a vehicle unit, related to controls performed using this VU (Annex 1C requirement 126). + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +A set of signatures plus metadata used in the download protocol. + + + + + + + + + id=0x7602,0x7622,0x7632;generation=1,2,3 + Positive Response Transfer Data Activities + + + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The date and time of the download plus metadata as used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The OdometerValueMidnight plus metadata used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName=vuCardIWRecords + +Generation 2: +Information, stored in a vehicle unit, related to insertion and withdrawal cycles of driver cards or of workshop cards in the vehicle unit (Annex 1C requirement 103). + + + + + generation=1,*;isRecordArray=0,1 + +Generation 2: +Information, stored in a VU, related to changes of activity and/or changes of driving status and/or changes of card status for a given calendar day (Annex 1C requirement 105, 106, 107) and to slots status at 00:00 that day. + + + + + generation=1,*;isRecordArray=0,1;recordName=vuPlaceDailyWorkPeriodRecords + +Generation 2: +Information, stored in a vehicle unit, related to places where drivers begin or end a daily work period (Annex 1C requirement 108 and 110). + + + + + generation=2..;isRecordArray=1;recordName=vuGnssADRecord + +Generation 2: +Information, stored in a vehicle unit, related to the GNSS position of the vehicle if the accumulated driving time reaches a multiple of three hours (Annex IC requirement 108 and 110). + + + + + + + + + + generation=1,*;isRecordArray=0,1;recordName=specificConditionRecords + +Generation 2: +Information, stored in a vehicle unit, related to specific conditions (Annex 1C requirement 130). + + + + + generation=3..;isRecordArray=1;recordName=vuBorderCrossingRecord + +Generation 2, version 2: +Information, stored in a vehicle unit, related to border crossings of the vehicle (Annex IC requirement 133c). + + + + + + + + + + generation=3..;isRecordArray=1;recordName=vuLoadUnloadRecord + +Generation 2, version 2: +Information, stored in a vehicle unit, related to a load/unload operation vehicle entered (Annex IC requirement 133h). + + + + + + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +A set of signatures plus metadata used in the download protocol. + + + + + + + + + id=0x7603,0x7623,0x7633;generation=1,2,3 + Positive Response Transfer Data Events and Faults + + + + + + + generation=1,*;isRecordArray=0,1;recordName=vuFaultRecords + +Generation 2: +Information, stored in a vehicle unit, related to faults (Annex 1C requirement 118). + + + + + generation=1,*;isRecordArray=0,1;recordName=vuEventRecords + +Generation 2: +Information, stored in a vehicle unit, related to events (Annex 1C requirement 117 except over speeding event). + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The VuOverSpeedingControlData plus metadata used in the download protocol. + + + + + generation=1,*;isRecordArray=0,1;recordName=vuOverSpeedingEventRecords + +Generation 2: +Information, stored in a vehicle unit, related to over speeding events (Annex 1C requirement 117). + + + + + generation=1,*;isRecordArray=0,1;recordName=vuTimeAdjustmentRecords + +Generation 2: +Information, stored in a vehicle unit, related to time adjustments performed outside the frame of a regular calibration (Annex 1C requirement 124 and 125). + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +A set of signatures plus metadata used in the download protocol. + + + + + + + + + id=0x7604,0x7624;generation=1,2 + Positive Response Transfer Data Detailed Speed + + + + + + + generation=1,*;isRecordArray=0,1 + +Generation 2: +Information, stored in a vehicle unit, related to the detailed speed of the vehicle. + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +A set of signatures plus metadata used in the download protocol. + + + + + + + + + id=0x7605,0x7625,0x7635;generation=1,2,3 + Positive Response Transfer Data Technical Data + + + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +The VuIdentification plus metadata used in the download protocol. + + + + + generation=1 + + + + + generation=2..;isRecordArray=1;recordName=sensorPairedRecord + +Generation 2: +A set of SensorPairedRecord plus metadata used in the download protocol. + + + + + + + + + + generation=2..;isRecordArray=1;recordName=sensorExternalGnssCoupledRecord + +Generation 2: +A set of SensorExternalGNSSCoupledRecord plus metadata used in the download protocol. + + + + + + + + + + generation=1,*;isRecordArray=0,1;recordName=vuCalibrationRecords + +Generation 2: +Information, stored in a vehicle unit, related to the calibrations of the recording equipment (Annex 1C requirement 119 and 120). + + + + + generation=2..;isRecordArray=1;recordName=vuCardRecord + +Generation 2: +Information stored in a vehicle unit about the tachograph cards used with this VU. This information is intended for the analysis of VU — card problems (Annex 1C requirement 132). + + + + + + + + + + generation=2..;isRecordArray=1;recordName=vuItsConsentRecord + +Generation 2: +Information, stored in a vehicle unit, related to drivers' consent on the usage of Intelligent Transport Systems (Annex 1C requirement 200). + + + + + + + + + + generation=2..;isRecordArray=1;recordName=vuPowerSupplyInterruptionRecord + +Generation 2: +Information, stored in a vehicle unit, related to Power Supply Interruption events (Annex 1C requirement 117). + + + + + + + + + + generation=1,*;isRecordArray=0,1;recordName= + +Generation 2: +A set of signatures plus metadata used in the download protocol. + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7611 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7612 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7613 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7614 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7615 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7616 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7618 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x7619 + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x761A + + + + + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x761B + + + + + + + + + + + + + + manufacturer specific ID (VDO tachograph) + id=0x761C + + + + + + + + + + + + + + + + + + + + + + + + + + + +Driver card applications + + + + + + + id=0x050100,0x050102;generation=1,2 + + + + + + + id=0x050101,0x050103;generation=1,2 + + + + + + + + + id=0xc10000;generation=1 + + + + + + + id=0xc10001;generation=1 + + + + + + + + + id=0xc10002;generation=2.. + + + + + + + id=0xc10003;generation=2.. + + + + + + + + + id=0xc10102;generation=2.. + + + + + + + id=0xc10103;generation=2.. + + + + + + + + + id=0xc10800,0xc10802;generation=1,2 + + + + + + + id=0xc10801,0xc10803;generation=1,2 + + + + + + + + + id=0xc10902;generation=2.. + + + + + + + id=0xc10903;generation=2.. + + + + + + + + + id=0x052000,0x052002;generation=1,2 + + + + + + + + id=0x052001,0x052003;generation=1,2 + + + + + + + + + id=0x050e00,0x050e02;generation=1,2 + + + + + + + id=0x050e01,0x050e03;generation=1,2 + + + + + + + + + id=0x052100,0x052102;generation=1,2 + + + + + + + id=0x052101,0x052103;generation=1,2 + + + + + + + + + id=0x050200,0x050202;generation=1,2 + + + + + + + id=0x050201,0x050203;generation=1,2 + + + + + + + + + id=0x050300,0x050302;generation=1,2 + + + + + + + id=0x050301,0x050303;generation=1,2 + + + + + + + + + id=0x050400,0x050402;generation=1,2 + + + + + + + id=0x050401,0x050403;generation=1,2 + + + + + + + + + id=0x050500,0x050502;generation=1,2 + + + + + + + id=0x050501,0x050503;generation=1,2 + + + + + + + + + id=0x050600,0x050602;generation=1,2 + + + + + + + id=0x050601,0x050603;generation=1,2 + + + + + + + + + id=0x050700,0x050702;generation=1,2 + + + + + + + id=0x050701,0x050703;generation=1,2 + + + + + + + + + id=0x050800,0x050802;generation=1,2 + + + + + + + id=0x050801,0x050803;generation=1,2 + + + + + + + + + id=0x052200,0x052202;generation=1,2 + + + + + + + id=0x052201,0x052203;generation=1,2 + + + + + + + + + id=0x052302;generation=2.. + + + + + + + id=0x052303;generation=2.. + + + + + + + + + id=0x052402;generation=2.. + + + + + + + id=0x052403;generation=2.. + + + + + + + + + id=0x052502;generation=3 + + + + + + + id=0x052503;generation=3 + + + + + + + + + id=0x052602;generation=3 + + + + + + + id=0x052603;generation=3 + + + + + + + + + id=0x052702;generation=3 + + + + + + + id=0x052703;generation=3 + + + + + + + + + id=0x052802;generation=3 + + + + + + + id=0x052803;generation=3 + + + + + + + + + id=0x052902;generation=3 + + + + + + + id=0x052903;generation=3 + + + + + + + + + id=0x053002;generation=3 + + + + + + + id=0x053003;generation=3 + + + + + + + + + id=0x054002;generation=3 + + + + + + + id=0x054003;generation=3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Workshop card applications + + + + + + + id=0x050100,0x050102;generation=1,2 + + + + + + + id=0x050101,0x050103;generation=1,2 + + + + + + + + + id=0xc10000;generation=1 + + + + + + + id=0xc10001;generation=1 + + + + + + + + + id=0xc10002;generation=2.. + + + + + + + id=0xc10003;generation=2.. + + + + + + + + + id=0xc10102;generation=2.. + + + + + + + id=0xc10103;generation=2.. + + + + + + + + + id=0xc10800,0xc10802;generation=1,2 + + + + + + + id=0xc10801,0xc10803;generation=1,2 + + + + + + + + + id=0xc10902;generation=2.. + + + + + + + id=0xc10903;generation=2.. + + + + + + + + + id=0x052000,0x052002;generation=1,2 + + + + + + + + id=0x052001,0x052003;generation=1,2 + + + + + + + + + id=0x050900,0x050902;generation=1,2 + + + + + + + id=0x050901,0x050903;generation=1,2 + + + + + + + + + id=0x050a00,0x050a02;generation=1,2 + + + + + + + id=0x050a01,0x050a03;generation=1,2 + + + + + + + + + id=0x050b00,0x050b02;generation=1,2 + + + + + + + id=0x050b01,0x050b03;generation=1,2 + + + + + + + + + id=0x050200,0x050202;generation=1,2 + + + + + + + id=0x050201,0x050203;generation=1,2 + + + + + + + + + id=0x050300,0x050302;generation=1,2 + + + + + + + id=0x050301,0x050303;generation=1,2 + + + + + + + + + id=0x050400,0x050402;generation=1,2 + + + + + + + id=0x050401,0x050403;generation=1,2 + + + + + + + + + id=0x050500,0x050502;generation=1,2 + + + + + + + id=0x050501,0x050503;generation=1,2 + + + + + + + + + id=0x050600,0x050602;generation=1,2 + + + + + + + id=0x050601,0x050603;generation=1,2 + + + + + + + + + id=0x050700,0x050702;generation=1,2 + + + + + + + id=0x050701,0x050703;generation=1,2 + + + + + + + + + id=0x050800,0x050802;generation=1,2 + + + + + + + id=0x050801,0x050803;generation=1,2 + + + + + + + + + id=0x052200,0x052202;generation=1,2 + + + + + + + id=0x052201,0x052203;generation=1,2 + + + + + + + + + id=0x052302;generation=2.. + + + + + + + id=0x052303;generation=2.. + + + + + + + + + id=0x052402;generation=2.. + + + + + + + id=0x052403;generation=2.. + + + + + + + + + id=0x052502;generation=3 + + + + + + + id=0x052503;generation=3 + + + + + + + + + id=0x052602;generation=3 + + + + + + + id=0x052603;generation=3 + + + + + + + + + id=0x052702;generation=3 + + + + + + + id=0x052703;generation=3 + + + + + + + + + id=0x052802;generation=3 + + + + + + + id=0x052803;generation=3 + + + + + + + + + id=0x052902;generation=3 + + + + + + + id=0x052903;generation=3 + + + + + + + + + id=0x053002;generation=3 + + + + + + + id=0x053003;generation=3 + + + + + + + + + id=0x053102;generation=3 + + + + + + + id=0x053103;generation=3 + + + + + + + + + id=0x054002;generation=3 + + + + + + + id=0x054003;generation=3 + + + + + + + + + + + + + + + + + + + + + + +Control card applications + + + + + + + id=0x050100,0x050102;generation=1,2 + + + + + + + id=0x050101,0x050103;generation=1,2 + + + + + + + + + id=0xc10000;generation=1 + + + + + + + id=0xc10001;generation=1 + + + + + + + + + id=0xc10002;generation=2.. + + + + + + + id=0xc10003;generation=2.. + + + + + + + + + id=0xc10800,0xc10802;generation=1,2 + + + + + + + id=0xc10801,0xc10803;generation=1,2 + + + + + + + + + id=0xc10902;generation=2.. + + + + + + + id=0xc10903;generation=2.. + + + + + + + + + id=0x052000,0x052002;generation=1,2 + + + + + + + + id=0x052001,0x052003;generation=1,2 + + + + + + + + + id=0x050c00,0x050c02;generation=1,2 + + + + + + + id=0x050c01,0x050c03;generation=1,2 + + + + + + + + + id=0x052502;generation=3 + + + + + + + id=0x052503;generation=3 + + + + + + + + + id=0x054002;generation=3 + + + + + + + id=0x054003;generation=3 + + + + + + + + + + + + + + +Company card applications + + + + + + + id=0x050100,0x050102;generation=1,2 + + + + + + + id=0x050101,0x050103;generation=1,2 + + + + + + + + + id=0xc10000;generation=1 + + + + + + + id=0xc10001;generation=1 + + + + + + + + + id=0xc10002;generation=2.. + + + + + + + id=0xc10003;generation=2.. + + + + + + + + + id=0xc10800,0xc10802;generation=1,2 + + + + + + + id=0xc10801,0xc10803;generation=1,2 + + + + + + + + + id=0xc10902;generation=2.. + + + + + + + id=0xc10903;generation=2.. + + + + + + + + + id=0x052000,0x052002;generation=1,2 + + + + + + + + id=0x052001,0x052003;generation=1,2 + + + + + + + + + id=0x050d00,0x050d02;generation=1,2 + + + + + + + id=0x050d01,0x050d03;generation=1,2 + + + + + + + + + id=0x052502;generation=3 + + + + + + + id=0x052503;generation=3 + + + + + + + + + id=0x054002;generation=3 + + + + + + + id=0x054003;generation=3 + + + + + + + + + + + + diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java new file mode 100644 index 0000000..176aee4 --- /dev/null +++ b/src/test/java/at/procon/eventhub/tachographfilesession/api/TachographFileSessionControllerTest.java @@ -0,0 +1,82 @@ +package at.procon.eventhub.tachographfilesession.api; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto; +import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse; +import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto; +import at.procon.eventhub.tachographfilesession.model.ExtractionStats; +import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService; +import java.time.Instant; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +class TachographFileSessionControllerTest { + + @Test + void uploadsSessionListsDriversAndDeletes() throws Exception { + TachographFileSessionService service = org.mockito.Mockito.mock(TachographFileSessionService.class); + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TachographFileSessionController(service)) + .setControllerAdvice(new TachographFileSessionExceptionHandler()) + .build(); + UUID sessionId = UUID.randomUUID(); + TachographFileDriverSummaryDto driver = new TachographFileDriverSummaryDto("12:123", "Muster", "Max", "12", "123", 3, 2); + TachographFileSessionSummaryDto summary = new TachographFileSessionSummaryDto( + sessionId, + "default", + "legalrequirements-drivercard", + "sample", + "sample.ddd", + true, + "42", + new ExtractionStats(1, 3, 2, 2, 2, 0), + List.of(driver), + List.of(), + Instant.parse("2026-05-12T10:00:00Z"), + Instant.parse("2026-05-12T14:00:00Z") + ); + when(service.createSession(org.mockito.ArgumentMatchers.any(), eq("default"), eq("src"), eq("sample"))) + .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.deleteSession(sessionId)).thenReturn(new TachographFileSessionDeleteResponse(sessionId, true)); + + mockMvc.perform(multipart("/api/eventhub/tachograph-file-sessions") + .file(new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes())) + .param("tenantKey", "default") + .param("sourceInstanceKey", "src") + .param("sessionLabel", "sample")) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.session.sessionId").value(sessionId.toString())); + + mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}", sessionId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sessionId").value(sessionId.toString())); + + mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers", sessionId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.drivers[0].driverKey").value("12:123")); + + mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}/drivers/{driverKey}", sessionId, "12:123")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.driverKey").value("12:123")); + + mockMvc.perform(delete("/api/eventhub/tachograph-file-sessions/{sessionId}", sessionId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.deleted").value(true)); + } +} diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java new file mode 100644 index 0000000..a0cd1a9 --- /dev/null +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlExtractionServiceTest.java @@ -0,0 +1,52 @@ +package at.procon.eventhub.tachographfilesession.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession; +import at.procon.eventhub.tachographfilesession.model.TachographFileSession; +import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import org.junit.jupiter.api.Test; + +class DriverCardXmlExtractionServiceTest { + + private final TachographXmlParser parser = new TachographXmlParser(); + private final DriverCardXmlExtractionService service = new DriverCardXmlExtractionService( + new DriverKeyFactory(), + new VehicleKeyFactory() + ); + + @Test + void extractsDriverCardVehiclesAndActivities() { + TachographFileSession session = service.extract( + parser.parseDriverCardXml(DriverCardXmlSamples.validDriverCardXml()), + new TachographFileSessionMetadata( + "default", + "legalrequirements-drivercard", + "sample", + "sample.ddd", + "abc", + 10, + "42", + "def", + true, + null + ), + Instant.now(), + Instant.now().plus(4, ChronoUnit.HOURS) + ); + + assertThat(session.driversByKey()).hasSize(1); + DriverExtractionSession driver = session.driversByKey().values().iterator().next(); + assertThat(driver.driverKey()).isEqualTo("12:12345678901200"); + assertThat(driver.driver().surname()).isEqualTo("Muster"); + assertThat(driver.driverCard().sourceDriverCardId()).isEqualTo("CARD:12:12345678901200"); + assertThat(driver.vehicleRegistrations()).hasSize(2); + assertThat(driver.vehicles()).hasSize(2); + assertThat(driver.cardVehicleUsageIntervals()).hasSize(2); + assertThat(driver.cardActivityIntervals()).hasSize(3); + assertThat(driver.cardActivityIntervals().get(0).registrationKey()).isEqualTo("12:W-12345A"); + assertThat(driver.cardActivityIntervals().get(2).registrationKey()).isEqualTo("12:W-54321B"); + } +} diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlSamples.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlSamples.java new file mode 100644 index 0000000..7fd4df4 --- /dev/null +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/DriverCardXmlSamples.java @@ -0,0 +1,115 @@ +package at.procon.eventhub.tachographfilesession.service; + +final class DriverCardXmlSamples { + + private DriverCardXmlSamples() { + } + + static String validDriverCardXml() { + return """ + + + + 12 + + 123456789012 + 0 + 0 + + + Authority + + 2026-04-01T00:00:00Z + 2026-04-01T00:00:00Z + 2031-04-01T00:00:00Z + + + + Muster + Max + + + 1985 + 06 + 15 + + de + + + + + + Vienna + 12 + B1234567 + + + + + + 1 + + 1000 + 1100 + 2026-04-01T08:00:00Z + 2026-04-01T11:59:59Z + + 12 + W-12345A + + 0001 + VIN12345678901234 + + + 1101 + 1200 + 2026-04-01T12:00:00Z + 2026-04-01T18:00:00Z + + 12 + W-54321B + + 0002 + VIN99999999999999 + + + + + + + 0 + 1 + + 2026-04-01T00:00:00Z + 0001 + 200 + 3 + + DRIVER + SINGLE + INSERTED + WORK + 08:00:00Z + + + DRIVER + SINGLE + INSERTED + DRIVING + 09:00:00Z + + + DRIVER + SINGLE + INSERTED + BREAK/REST + 12:30:00Z + + + + + + + """; + } +} diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionServiceTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionServiceTest.java new file mode 100644 index 0000000..8613045 --- /dev/null +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/TachographFileSessionServiceTest.java @@ -0,0 +1,52 @@ +package at.procon.eventhub.tachographfilesession.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import at.procon.eventhub.config.EventHubProperties; +import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse; +import at.procon.eventhub.tachographfilesession.model.ExtractionStats; +import at.procon.eventhub.tachographfilesession.model.TachographFileSession; +import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.mock.web.MockMultipartFile; + +class TachographFileSessionServiceTest { + + @Test + void createsAndLoadsSession() { + EventHubProperties properties = new EventHubProperties(); + TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties); + LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class); + TachographXmlParser parser = Mockito.mock(TachographXmlParser.class); + DriverCardXmlExtractionService extractor = Mockito.mock(DriverCardXmlExtractionService.class); + TachographFileSessionService service = new TachographFileSessionService(properties, repository, client, parser, extractor); + + MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes(StandardCharsets.UTF_8)); + when(client.uploadDriverCard(any(), eq("sample.ddd"))).thenReturn(new LegalRequirementsUploadResult("42", "")); + TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class); + when(parser.parseDriverCardXml("")).thenReturn(parsed); + TachographFileSession extracted = new TachographFileSession( + UUID.randomUUID(), + new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "sample.ddd", "a", 3, "42", "b", true, null), + Map.of(), + new ExtractionStats(0, 0, 0, 0, 0, 0), + java.util.List.of(), + Instant.now(), + Instant.now().plusSeconds(10) + ); + when(extractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted); + + CreateTachographFileSessionResponse response = service.createSession(file, null, null, "sample"); + + assertThat(response.session().sessionId()).isEqualTo(extracted.sessionId()); + assertThat(service.getSession(extracted.sessionId()).sessionId()).isEqualTo(extracted.sessionId()); + } +} diff --git a/src/test/java/at/procon/eventhub/tachographfilesession/service/TachographXmlParserTest.java b/src/test/java/at/procon/eventhub/tachographfilesession/service/TachographXmlParserTest.java new file mode 100644 index 0000000..aab2085 --- /dev/null +++ b/src/test/java/at/procon/eventhub/tachographfilesession/service/TachographXmlParserTest.java @@ -0,0 +1,27 @@ +package at.procon.eventhub.tachographfilesession.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class TachographXmlParserTest { + + private final TachographXmlParser parser = new TachographXmlParser(); + + @Test + void parsesValidDriverCardXml() { + TachographXmlParser.ParsedTachographXml parsed = parser.parseDriverCardXml(DriverCardXmlSamples.validDriverCardXml()); + + assertThat(parsed.rootElementName()).isEqualTo("DriverCard"); + assertThat(parsed.document().getDocumentElement().getNodeName()).isEqualTo("DriverCard"); + } + + @Test + void rejectsInvalidXmlAgainstSchema() { + String invalid = ""; + + assertThatThrownBy(() -> parser.parseDriverCardXml(invalid)) + .isInstanceOf(TachographXmlValidationException.class); + } +}