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: relatedTrainInformationRequest (#216) #468

Merged
merged 1 commit into from
Dec 17, 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
@@ -1,7 +1,9 @@
package ch.sbb.sferamock.messages.services;

import ch.sbb.sferamock.adapters.sfera.model.v0201.G2BEventPayload;
import ch.sbb.sferamock.adapters.sfera.model.v0201.RelatedTrainInformation;
import ch.sbb.sferamock.messages.common.XmlHelper;
import ch.sbb.sferamock.messages.model.TrainIdentification;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
Expand All @@ -10,6 +12,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.boot.ApplicationArguments;
Expand All @@ -31,6 +35,36 @@ public EventRepository(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}

private static String extractOperationalNumber(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(2);
if (directoryOperationalNumber != null && directoryOperationalNumber.equals(fileOperationalNumber)) {
return directoryOperationalNumber;
}
}
throw new RuntimeException("Operational number extraction in Event repository failed for file: " + filename);
}

private static int extractOffsetMs(String filename) {
Pattern pattern = Pattern.compile(OFFSET_XML_REGEX);
Matcher matcher = pattern.matcher(filename);
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
}
throw new RuntimeException("Offset extraction in Event repository failed for file: " + filename);
}

public Optional<RelatedTrainInformation> getRelatedTrainInformation(TrainIdentification trainIdentification) {
return Optional.ofNullable(events.get(trainIdentification.operationalNumber()))
.flatMap(trainEvents -> trainEvents.stream()
.map(event -> event.payload.getRelatedTrainInformation())
.filter(Objects::nonNull)
.findFirst());
}

@Override
public void run(ApplicationArguments args) throws Exception {
importEvents();
Expand All @@ -57,28 +91,6 @@ private void importEvents() throws IOException {
}
}

private static String extractOperationalNumber(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(2);
if (directoryOperationalNumber != null && directoryOperationalNumber.equals(fileOperationalNumber)) {
return directoryOperationalNumber;
}
}
throw new RuntimeException("Operational number extraction in Event repository failed for file: " + filename);
}

private static int extractOffsetMs(String filename) {
Pattern pattern = Pattern.compile(OFFSET_XML_REGEX);
Matcher matcher = pattern.matcher(filename);
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
}
throw new RuntimeException("Offset extraction in Event repository failed for file: " + filename);
}

public record Event(int offsetMs, G2BEventPayload payload) {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void run(ApplicationArguments args) throws Exception {
importJps();
}

// company and startDate is ignored
public Optional<JourneyProfile> getJourneyProfile(TrainIdentification trainIdentification) {
return Optional.ofNullable(journeyProfiles.get(trainIdentification.operationalNumber()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void run(ApplicationArguments args) throws Exception {
importSps();
}

// segment version and company is ignored
public Optional<SegmentProfile> getSegmentProfile(SegmentIdentification spId) {
return Optional.ofNullable(segmentProfiles.get(spId.id()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ch.sbb.sferamock.messages.services;

import ch.sbb.sferamock.adapters.sfera.model.v0201.JourneyProfile;
import ch.sbb.sferamock.adapters.sfera.model.v0201.RelatedTrainInformation;
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;
Expand Down Expand Up @@ -31,17 +32,19 @@ public class SferaApplicationService {
private final JourneyProfileRepository journeyProfileRepository;
private final SegmentProfileRepository segmentProfileRepository;
private final TrainCharacteristicsRepository trainCharacteristicsRepository;
private final EventRepository eventRepository;

public SferaApplicationService(ReplyPublisher replyPublisher, RequestContextRepository requestContextRepository, OperationModeSelector operationModeSelector,
RegistrationService registrationService, JourneyProfileRepository journeyProfileRepository, SegmentProfileRepository segmentProfileRepository,
TrainCharacteristicsRepository trainCharacteristicsRepository) {
TrainCharacteristicsRepository trainCharacteristicsRepository, EventRepository eventRepository) {
this.replyPublisher = replyPublisher;
this.requestContextRepository = requestContextRepository;
this.operationModeSelector = operationModeSelector;
this.registrationService = registrationService;
this.journeyProfileRepository = journeyProfileRepository;
this.segmentProfileRepository = segmentProfileRepository;
this.trainCharacteristicsRepository = trainCharacteristicsRepository;
this.eventRepository = eventRepository;
}

private static JourneyProfile unavailableJourneyProfile() {
Expand Down Expand Up @@ -130,6 +133,23 @@ public void processTrainCharacteristicsRequest(List<TrainCharacteristicsIdentifi
publishTrainCharacteristics(trainCharacteristics, correlationId, requestContext);
}

public void processRelatedTrainInformationRequest(List<TrainIdentification> trainIdentifications, RequestContext requestContext) {
if (!registrationService.isRegistered(requestContext.clientId())) {
publishErrorMessageUnregisteredClient(requestContext);
return;
}

var correlationId = UUID.randomUUID();
requestContextRepository.storeRequestContext(correlationId, requestContext);

List<RelatedTrainInformation> relatedTrainInformations = trainIdentifications.stream().map(trainIdentification ->
eventRepository.getRelatedTrainInformation(trainIdentification).orElse(null))
.filter(Objects::nonNull)
.toList();

publishRelatedTrainInformations(relatedTrainInformations, 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 Down Expand Up @@ -166,6 +186,20 @@ private void publishTrainCharacteristicsResponse(List<TrainCharacteristics> trai
}
}

private void publishRelatedTrainInformations(List<RelatedTrainInformation> relatedTrainInformations, UUID correlationId, RequestContext requestContext) {
requestContextRepository.getRequestContext(correlationId)
.ifPresentOrElse(it -> publishRelatedTrainInformationsResponse(relatedTrainInformations, it),
() -> replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext));
}

private void publishRelatedTrainInformationsResponse(List<RelatedTrainInformation> relatedTrainInformations, RequestContext requestContext) {
if (!relatedTrainInformations.isEmpty()) {
replyPublisher.publishRelatedTrainInformations(relatedTrainInformations, requestContext);
} else {
replyPublisher.publishErrorMessage(SferaErrorCodes.COULD_NOT_PROCESS_DATA, requestContext);
}
}

private void publishOk(RequestContext requestContext) {
replyPublisher.publishOkMessage(requestContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ch.sbb.sferamock.adapters.sfera.model.v0201.B2GRequest;
import ch.sbb.sferamock.adapters.sfera.model.v0201.JPRequest;
import ch.sbb.sferamock.adapters.sfera.model.v0201.RelatedTrainInformationRequest;
import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAB2GEventMessage;
import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAB2GReplyMessage;
import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAB2GRequestMessage;
Expand Down Expand Up @@ -113,6 +114,10 @@ private void processB2GRequest(SFERAB2GRequestMessage request, String topic) {
processTrainCharacteristicsRequest(b2GRequest.getTCRequest(), requestContext);
return;
}
if (b2GRequest != null && b2GRequest.getRelatedTrainInformationRequest() != null && !b2GRequest.getRelatedTrainInformationRequest().isEmpty()) {
processRelatedTrainInformationRequest(b2GRequest.getRelatedTrainInformationRequest(), 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 @@ -163,4 +168,12 @@ private void processTrainCharacteristicsRequest(List<TCRequest> tcRequests, Requ
new CompanyCode(tcRequest.getTCRUID()))).toList();
sferaApplicationService.processTrainCharacteristicsRequest(trainCharacteristicsIdentifications, requestContext);
}

private void processRelatedTrainInformationRequest(List<RelatedTrainInformationRequest> relatedTrainInformationRequests, RequestContext requestContext) {
var trainIdentifications = relatedTrainInformationRequests.stream()
.map(relatedTrainInformationRequest -> relatedTrainInformationRequest.getTrainIdentification().getOTNID())
.map(otnId -> new TrainIdentification(new CompanyCode(otnId.getCompany()), otnId.getOperationalTrainNumber(), XmlDateHelper.toLocalDate(otnId.getStartDate())))
.toList();
sferaApplicationService.processRelatedTrainInformationRequest(trainIdentifications, requestContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.springframework.util.MimeTypeUtils.APPLICATION_XML;

import ch.sbb.sferamock.adapters.sfera.model.v0201.JourneyProfile;
import ch.sbb.sferamock.adapters.sfera.model.v0201.RelatedTrainInformation;
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;
Expand Down Expand Up @@ -55,6 +56,12 @@ public void publishTrainCharacteristics(List<TrainCharacteristics> trainCharacte
publishReplyMessage(reply, requestContext);
}

public void publishRelatedTrainInformations(List<RelatedTrainInformation> relatedTrainInformations, RequestContext requestContext) {
var header = sferaMessageCreator.createMessageHeader(UUID.randomUUID(), requestContext.tid(), requestContext.incomingMessageId());
var reply = sferaMessageCreator.createRelatedTrainInformationReplyMessage(relatedTrainInformations, 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 @@ -12,6 +12,7 @@
import ch.sbb.sferamock.adapters.sfera.model.v0201.MessageHeader;
import ch.sbb.sferamock.adapters.sfera.model.v0201.OTNIDComplexType;
import ch.sbb.sferamock.adapters.sfera.model.v0201.Recipient;
import ch.sbb.sferamock.adapters.sfera.model.v0201.RelatedTrainInformation;
import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAG2BEventMessage;
import ch.sbb.sferamock.adapters.sfera.model.v0201.SFERAG2BReplyMessage;
import ch.sbb.sferamock.adapters.sfera.model.v0201.SegmentProfile;
Expand Down Expand Up @@ -195,6 +196,16 @@ public SFERAG2BReplyMessage createTrainCharacteristicsReplyMessage(List<TrainCha
return result;
}

public SFERAG2BReplyMessage createRelatedTrainInformationReplyMessage(List<RelatedTrainInformation> relatedTrainInformations, MessageHeader header) {
var result = new SFERAG2BReplyMessage();
result.setMessageHeader(header);
var payload = new G2BReplyPayload();
relatedTrainInformations.forEach(relatedTrainInformation -> relatedTrainInformation.getOwnTrain().setTrainIdentification(header.getTrainIdentification()));
payload.getRelatedTrainInformation().addAll(relatedTrainInformations);
result.setG2BReplyPayload(payload);
return result;
}

public SFERAG2BReplyMessage createOkMessage(MessageHeader header) {
var result = new SFERAG2BReplyMessage();
result.setMessageHeader(header);
Expand Down
4 changes: 3 additions & 1 deletion webapp/src/app/sfera-observer/sfera-observer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,15 @@ export class SferaObserverComponent implements OnDestroy {
return this.getSegmentProfiles(document);
} else if (this.containsElement(document, 'TrainCharacteristics')) {
return this.getTrainCharacteristics(document);
} else if (this.containsElement(document, 'RelatedTrainInformation')) {
return 'RelatedTrainInformation';
}
} else if (type == "SFERA_B2G_RequestMessage") {
if (this.isHandshakeRequest(document)) {
return `HS-REQUEST`;
}

const requestTypes = ['JP_Request', 'SP_Request', 'TC_Request']
const requestTypes = ['JP_Request', 'SP_Request', 'TC_Request', 'RelatedTrainInformationRequest']
const requestedTypes: string[] = [];

for (const requestType of requestTypes) {
Expand Down
Loading