Skip to content

Commit

Permalink
feat: train characteristics request/reply
Browse files Browse the repository at this point in the history
  • Loading branch information
mghilardelli committed Dec 13, 2024
1 parent 6cdb7d7 commit 29ff808
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -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) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -98,6 +104,23 @@ public void processSegmentProfileRequest(List<SegmentIdentification> segmentIden
publishSegmentProfile(segmentProfiles, correlationId, requestContext);
}

public void processTrainCharacteristicsRequest(List<TrainCharacteristicsIdentification> 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> journeyProfile, UUID correlationId, RequestContext requestContext) {
requestContextRepository.getRequestContext(correlationId)
.ifPresentOrElse(it -> publishJourneyProfileResponse(journeyProfile, it), () -> replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext));
Expand All @@ -121,6 +144,19 @@ private void publishSegmentProfileResponse(List<SegmentProfile> segmentProfiles,
}
}

private void publishTrainCharacteristics(List<TrainCharacteristics> 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> 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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, TrainCharacteristics> 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<TrainCharacteristics> 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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,6 +89,11 @@ public void processIncomingMessage(Message<byte[]> 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.");
}
}
Expand Down Expand Up @@ -139,4 +146,13 @@ private void processSegmentProfileRequest(List<SPRequest> spRequests, RequestCon
new CompanyCode(spRequest.getSPZone().getIMID()))).toList();
sferaApplicationService.processSegmentProfileRequest(segmentIdentifications, requestContext);
}

private void processTrainCharacteristicsRequest(List<TCRequest> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -48,6 +49,12 @@ public void publishSegmentProfile(List<SegmentProfile> segmentProfiles, RequestC
publishReplyMessage(reply, requestContext);
}

public void publishTrainCharacteristics(List<TrainCharacteristics> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -159,4 +160,13 @@ public SFERAG2BReplyMessage createSegmentProfileReplyMessage(List<SegmentProfile
result.setG2BReplyPayload(payload);
return result;
}

public SFERAG2BReplyMessage createTrainCharacteristicsReplyMessage(List<TrainCharacteristics> trainCharacteristics, MessageHeader header) {
var result = new SFERAG2BReplyMessage();
result.setMessageHeader(header);
var payload = new G2BReplyPayload();
payload.getTrainCharacteristics().addAll(trainCharacteristics);
result.setG2BReplyPayload(payload);
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<JourneyProfile JP_Status="Valid" JP_Version="1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../SFERA_3.0_custom.xsd">
<TrainIdentification>
<OTN_ID>
<Company>1085</Company>
<OperationalTrainNumber>T5</OperationalTrainNumber>
<StartDate>2022-01-04</StartDate>
</OTN_ID>
</TrainIdentification>
<SegmentProfileList SP_ID="T5_1" SP_VersionMajor="1" SP_VersionMinor="1" SP_Direction="Nominal">
<SP_Zone>
<IM_ID>0085</IM_ID>
</SP_Zone>
<TimingPointConstraints TP_StopSkipPass="Stopping_Point" TP_Information="None">
<TimingPointReference>
<TP_ID_Reference TP_ID="Genève-Aéroport"/>
</TimingPointReference>
</TimingPointConstraints>
<TimingPointConstraints TP_StopSkipPass="Stopping_Point" TP_Information="None">
<TimingPointReference>
<TP_ID_Reference TP_ID="Genève"/>
</TimingPointReference>
</TimingPointConstraints>
<TimingPointConstraints TP_StopSkipPass="Stopping_Point" TP_Information="None">
<TimingPointReference>
<TP_ID_Reference TP_ID="Gland"/>
</TimingPointReference>
</TimingPointConstraints>
<TrainCharacteristicsRef TC_ID="T5_1" TC_VersionMajor="1" TC_VersionMinor="1" location="0">
<TC_RU_ID>1185</TC_RU_ID>
</TrainCharacteristicsRef>
</SegmentProfileList>
</JourneyProfile>
Loading

0 comments on commit 29ff808

Please sign in to comment.