Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: train characteristics request/reply (#216) #465

Merged
merged 1 commit into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading