From 29ff80898a721da2da7fd44fb6ba17cf68d53460 Mon Sep 17 00:00:00 2001 From: mghilardelli Date: Fri, 13 Dec 2024 15:21:07 +0100 Subject: [PATCH] feat: train characteristics request/reply --- .../TrainCharacteristicsIdentification.java | 7 + .../services/SferaApplicationService.java | 38 ++- .../TrainCharacteristicsRepository.java | 69 +++++ .../sfera/IncomingMessageAdapter.java | 22 +- .../messages/sfera/ReplyPublisher.java | 7 + .../messages/sfera/SferaMessageCreator.java | 10 + .../T5_Breaking_series/SFERA_JP_T5.xml | 35 +++ .../T5_Breaking_series/SFERA_SP_T5_1.xml | 252 ++++++++++++++++++ .../T5_Breaking_series/SFERA_TC_T5_1.xml | 8 + 9 files changed, 444 insertions(+), 4 deletions(-) create mode 100644 sfera-mock/src/main/java/ch/sbb/sferamock/messages/model/TrainCharacteristicsIdentification.java create mode 100644 sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/TrainCharacteristicsRepository.java create mode 100644 sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_JP_T5.xml create mode 100644 sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_SP_T5_1.xml create mode 100644 sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_TC_T5_1.xml diff --git a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/model/TrainCharacteristicsIdentification.java b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/model/TrainCharacteristicsIdentification.java new file mode 100644 index 00000000..87aa14b9 --- /dev/null +++ b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/model/TrainCharacteristicsIdentification.java @@ -0,0 +1,7 @@ +package ch.sbb.sferamock.messages.model; + +import lombok.NonNull; + +public record TrainCharacteristicsIdentification(@NonNull String id, int majorVersion, int minorVersion, @NonNull CompanyCode ruId) { + +} diff --git a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/SferaApplicationService.java b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/SferaApplicationService.java index a43b3cb2..fec33de6 100644 --- a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/SferaApplicationService.java +++ b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/SferaApplicationService.java @@ -3,14 +3,17 @@ import ch.sbb.sferamock.adapters.sfera.model.v0201.JourneyProfile; import ch.sbb.sferamock.adapters.sfera.model.v0201.SPZoneComplexType; import ch.sbb.sferamock.adapters.sfera.model.v0201.SegmentProfile; +import ch.sbb.sferamock.adapters.sfera.model.v0201.TrainCharacteristics; import ch.sbb.sferamock.messages.common.SferaErrorCodes; import ch.sbb.sferamock.messages.model.HandshakeRejectReason; import ch.sbb.sferamock.messages.model.OperationMode; import ch.sbb.sferamock.messages.model.RequestContext; import ch.sbb.sferamock.messages.model.SegmentIdentification; +import ch.sbb.sferamock.messages.model.TrainCharacteristicsIdentification; import ch.sbb.sferamock.messages.model.TrainIdentification; import ch.sbb.sferamock.messages.sfera.ReplyPublisher; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import org.slf4j.Logger; @@ -27,15 +30,18 @@ public class SferaApplicationService { private final RegistrationService registrationService; private final JourneyProfileRepository journeyProfileRepository; private final SegmentProfileRepository segmentProfileRepository; + private final TrainCharacteristicsRepository trainCharacteristicsRepository; public SferaApplicationService(ReplyPublisher replyPublisher, RequestContextRepository requestContextRepository, OperationModeSelector operationModeSelector, - RegistrationService registrationService, JourneyProfileRepository journeyProfileRepository, SegmentProfileRepository segmentProfileRepository) { + RegistrationService registrationService, JourneyProfileRepository journeyProfileRepository, SegmentProfileRepository segmentProfileRepository, + TrainCharacteristicsRepository trainCharacteristicsRepository) { this.replyPublisher = replyPublisher; this.requestContextRepository = requestContextRepository; this.operationModeSelector = operationModeSelector; this.registrationService = registrationService; this.journeyProfileRepository = journeyProfileRepository; this.segmentProfileRepository = segmentProfileRepository; + this.trainCharacteristicsRepository = trainCharacteristicsRepository; } private static JourneyProfile unavailableJourneyProfile() { @@ -98,6 +104,23 @@ public void processSegmentProfileRequest(List segmentIden publishSegmentProfile(segmentProfiles, correlationId, requestContext); } + public void processTrainCharacteristicsRequest(List trainCharacteristicsIdentifications, RequestContext requestContext) { + if (!registrationService.isRegistered(requestContext.clientId())) { + publishErrorMessageUnregisteredClient(requestContext); + return; + } + + var correlationId = UUID.randomUUID(); + requestContextRepository.storeRequestContext(correlationId, requestContext); + + var trainCharacteristics = trainCharacteristicsIdentifications.stream() + .map(trainCharacteristicsIdentification -> trainCharacteristicsRepository.getTrainCharacteristics(trainCharacteristicsIdentification) + .orElse(null)) + .filter(Objects::nonNull) + .toList(); + publishTrainCharacteristics(trainCharacteristics, correlationId, requestContext); + } + private void publishJourneyProfile(Optional journeyProfile, UUID correlationId, RequestContext requestContext) { requestContextRepository.getRequestContext(correlationId) .ifPresentOrElse(it -> publishJourneyProfileResponse(journeyProfile, it), () -> replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext)); @@ -121,6 +144,19 @@ private void publishSegmentProfileResponse(List segmentProfiles, } } + private void publishTrainCharacteristics(List trainCharacteristics, UUID correlationId, RequestContext requestContext) { + requestContextRepository.getRequestContext(correlationId) + .ifPresentOrElse(it -> publishTrainCharacteristicsResponse(trainCharacteristics, it), () -> replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext)); + } + + private void publishTrainCharacteristicsResponse(List trainCharacteristics, RequestContext requestContext) { + if (!trainCharacteristics.isEmpty()) { + replyPublisher.publishTrainCharacteristics(trainCharacteristics, requestContext); + } else { + replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext); + } + } + private void publishErrorMessageUnregisteredClient(RequestContext requestContext) { log.warn("Received a request from an unregistered client id {}", requestContext.clientId()); replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext); diff --git a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/TrainCharacteristicsRepository.java b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/TrainCharacteristicsRepository.java new file mode 100644 index 00000000..fb16e91b --- /dev/null +++ b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/services/TrainCharacteristicsRepository.java @@ -0,0 +1,69 @@ +package ch.sbb.sferamock.messages.services; + +import ch.sbb.sferamock.adapters.sfera.model.v0201.TrainCharacteristics; +import ch.sbb.sferamock.messages.common.XmlHelper; +import ch.sbb.sferamock.messages.model.TrainCharacteristicsIdentification; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.stereotype.Service; + +@Service +public class TrainCharacteristicsRepository implements ApplicationRunner { + + private static final String XML_RESOURCES_CLASSPATH = "classpath:static_sfera_resources/*/SFERA_TC_*.xml"; + private static final String XML_REGEX = "/([a-zA-Z0-9]+)_\\w+/SFERA_TC_(([a-zA-Z0-9]+)_\\w+)\\.xml"; + + private final XmlHelper xmlHelper; + + Map trainCharacteristics = new HashMap<>(); + + public TrainCharacteristicsRepository(XmlHelper xmlHelper) { + this.xmlHelper = xmlHelper; + } + + public static String extractTcId(String filename) { + Pattern pattern = Pattern.compile(XML_REGEX); + Matcher matcher = pattern.matcher(filename); + if (matcher.find()) { + String directoryOperationalNumber = matcher.group(1); + String fileOperationalNumber = matcher.group(3); + if (directoryOperationalNumber != null && directoryOperationalNumber.equals(fileOperationalNumber)) { + return matcher.group(2); + } + } + throw new RuntimeException("TC id extraction failed for file: " + filename); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + importTcs(); + } + + public Optional getTrainCharacteristics(TrainCharacteristicsIdentification tcId) { + return Optional.ofNullable(trainCharacteristics.get(tcId.id())); + } + + private void importTcs() throws IOException { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + var resources = resolver.getResources(XML_RESOURCES_CLASSPATH); + for (var resource : resources) { + File file = resource.getFile(); + var tcId = extractTcId(file.getPath()); + try (InputStream in = new FileInputStream(file)) { + String xmlPayload = new String(in.readAllBytes()); + var tc = xmlHelper.xmlToObject(xmlPayload); + this.trainCharacteristics.put(tcId, (TrainCharacteristics) tc); + } + } + } +} diff --git a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/IncomingMessageAdapter.java b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/IncomingMessageAdapter.java index 74ca8bc9..a2511c41 100644 --- a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/IncomingMessageAdapter.java +++ b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/IncomingMessageAdapter.java @@ -6,14 +6,16 @@ import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAB2GReplyMessage; import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAB2GRequestMessage; import ch.sbb.sferamock.adapters.sfera.model.v0201.SPRequest; -import ch.sbb.sferamock.messages.services.SferaApplicationService; +import ch.sbb.sferamock.adapters.sfera.model.v0201.TCRequest; +import ch.sbb.sferamock.messages.common.SferaErrorCodes; import ch.sbb.sferamock.messages.common.XmlDateHelper; import ch.sbb.sferamock.messages.common.XmlHelper; import ch.sbb.sferamock.messages.model.CompanyCode; import ch.sbb.sferamock.messages.model.RequestContext; -import ch.sbb.sferamock.messages.common.SferaErrorCodes; -import ch.sbb.sferamock.messages.model.TrainIdentification; import ch.sbb.sferamock.messages.model.SegmentIdentification; +import ch.sbb.sferamock.messages.model.TrainCharacteristicsIdentification; +import ch.sbb.sferamock.messages.model.TrainIdentification; +import ch.sbb.sferamock.messages.services.SferaApplicationService; import com.solacesystems.jcsmp.impl.TopicImpl; import java.io.StringReader; import java.nio.charset.StandardCharsets; @@ -87,6 +89,11 @@ public void processIncomingMessage(Message message) { processSegmentProfileRequest(b2GRequest.getSPRequest(), requestContext); return; } + if (b2GRequest != null && b2GRequest.getTCRequest() != null && !b2GRequest.getTCRequest().isEmpty()) { + processTrainCharacteristicsRequest(b2GRequest.getTCRequest(), requestContext); + return; + } + log.warn("A B2G Request that is not a handshake should currently have exactly one jp or sp request. Request is ignored."); } } @@ -139,4 +146,13 @@ private void processSegmentProfileRequest(List spRequests, RequestCon new CompanyCode(spRequest.getSPZone().getIMID()))).toList(); sferaApplicationService.processSegmentProfileRequest(segmentIdentifications, requestContext); } + + private void processTrainCharacteristicsRequest(List tcRequests, RequestContext requestContext) { + var trainCharacteristicsIdentifications = tcRequests.stream().map(tcRequest -> new TrainCharacteristicsIdentification( + tcRequest.getTCID(), + tcRequest.getTCVersionMajor() != null ? tcRequest.getTCVersionMajor().intValue() : 0, + tcRequest.getTCVersionMinor() != null ? tcRequest.getTCVersionMinor().intValue() : 0, + new CompanyCode(tcRequest.getTCRUID()))).toList(); + sferaApplicationService.processTrainCharacteristicsRequest(trainCharacteristicsIdentifications, requestContext); + } } diff --git a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/ReplyPublisher.java b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/ReplyPublisher.java index 12d945bf..acbb9da4 100644 --- a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/ReplyPublisher.java +++ b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/ReplyPublisher.java @@ -5,6 +5,7 @@ import ch.sbb.sferamock.adapters.sfera.model.v0201.JourneyProfile; import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAG2BReplyMessage; import ch.sbb.sferamock.adapters.sfera.model.v0201.SegmentProfile; +import ch.sbb.sferamock.adapters.sfera.model.v0201.TrainCharacteristics; import ch.sbb.sferamock.messages.common.SferaErrorCodes; import ch.sbb.sferamock.messages.common.XmlHelper; import ch.sbb.sferamock.messages.model.HandshakeRejectReason; @@ -48,6 +49,12 @@ public void publishSegmentProfile(List segmentProfiles, RequestC publishReplyMessage(reply, requestContext); } + public void publishTrainCharacteristics(List trainCharacteristics, RequestContext requestContext) { + var header = sferaMessageCreator.createMessageHeader(UUID.randomUUID(), requestContext.tid(), requestContext.incomingMessageId()); + var reply = sferaMessageCreator.createTrainCharacteristicsReplyMessage(trainCharacteristics, header); + publishReplyMessage(reply, requestContext); + } + public void publishHandshakeAcknowledge(OperationMode.Connectivity connectivity, OperationMode.Architecture architecture, RequestContext requestContext) { diff --git a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/SferaMessageCreator.java b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/SferaMessageCreator.java index 0ee30f26..515fd8fe 100644 --- a/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/SferaMessageCreator.java +++ b/sfera-mock/src/main/java/ch/sbb/sferamock/messages/sfera/SferaMessageCreator.java @@ -14,6 +14,7 @@ import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAG2BReplyMessage; import ch.sbb.sferamock.adapters.sfera.model.v0201.SegmentProfile; import ch.sbb.sferamock.adapters.sfera.model.v0201.Sender; +import ch.sbb.sferamock.adapters.sfera.model.v0201.TrainCharacteristics; import ch.sbb.sferamock.adapters.sfera.model.v0201.TrainIdentificationComplexType; import ch.sbb.sferamock.adapters.sfera.model.v0201.UnavailableDASOperatingModes; import ch.sbb.sferamock.messages.common.XmlDateHelper; @@ -159,4 +160,13 @@ public SFERAG2BReplyMessage createSegmentProfileReplyMessage(List trainCharacteristics, MessageHeader header) { + var result = new SFERAG2BReplyMessage(); + result.setMessageHeader(header); + var payload = new G2BReplyPayload(); + payload.getTrainCharacteristics().addAll(trainCharacteristics); + result.setG2BReplyPayload(payload); + return result; + } } diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_JP_T5.xml b/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_JP_T5.xml new file mode 100644 index 00000000..59c002c1 --- /dev/null +++ b/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_JP_T5.xml @@ -0,0 +1,35 @@ + + + + + 1085 + T5 + 2022-01-04 + + + + + 0085 + + + + + + + + + + + + + + + + + + 1185 + + + diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_SP_T5_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_SP_T5_1.xml new file mode 100644 index 00000000..778f64ca --- /dev/null +++ b/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_SP_T5_1.xml @@ -0,0 +1,252 @@ + + + + 0085 + + + + + + CH + 3002 + + + + + + + CH + 3003 + + + + + + + CH + 3004 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CH + 3002 + + + + + + + + + + CH + 3003 + + + + + + + + + + CH + 3004 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_TC_T5_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_TC_T5_1.xml new file mode 100644 index 00000000..51ce7bf2 --- /dev/null +++ b/sfera-mock/src/main/resources/static_sfera_resources/T5_Breaking_series/SFERA_TC_T5_1.xml @@ -0,0 +1,8 @@ + + + 1185 + + +