Skip to content

Commit

Permalink
Merge pull request #4667 from mfdz/gtfs-rt-extension
Browse files Browse the repository at this point in the history
GTFS-RT extension to add completely new routes
  • Loading branch information
leonardehrenfried authored Jan 18, 2023
2 parents f5478ba + c2b46ab commit 37489ac
Show file tree
Hide file tree
Showing 33 changed files with 1,276 additions and 726 deletions.
16 changes: 13 additions & 3 deletions docs/sandbox/ActuatorAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ running OTP in a container.
The API will be at the endpoint `http://localhost:8080/otp/actuators` and follows the Spring Boot
actuator API standard.

### Configuration

To enable this you need to add the feature `ActuatorAPI`.

```json
// otp-config.json
{
"otpFeatures" : {
"ActuatorAPI": true
}
}
```

### Endpoints

#### /health
Expand All @@ -32,6 +45,3 @@ Prometheus metrics are returned using Micrometer. The default JVM and jersey met
Also, GraphQL timing metrics are exported under `graphql.timer.query` and `graphql.timer.resolver`,
if the GraphQL endpoints are enabled.

### Configuration

To enable this you need to add the feature `ActuatorAPI`.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@
import org.opentripplanner.transit.model.timetable.Trip;
import org.rutebanken.netex.model.BusSubmodeEnumeration;
import org.rutebanken.netex.model.RailSubmodeEnumeration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.org.siri.siri20.NaturalLanguageStringStructure;
import uk.org.siri.siri20.VehicleModesEnumeration;

public class AddedTripHelper {

private static final Logger LOG = LoggerFactory.getLogger(AddedTripHelper.class);

/**
* Method to create a Route. Commonly used to create a route if a realtime message
* refers to a route that is not in the transit model.
Expand Down Expand Up @@ -73,8 +69,6 @@ public static Route getRoute(
return routeBuilder.build();
}

public static void getStopTime(StopLocation stop) {}

public static String getFirstNameFromList(List<NaturalLanguageStringStructure> names) {
if (isNotNullOrEmpty(names)) {
return names.stream().findFirst().map(NaturalLanguageStringStructure::getValue).orElse("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.opentripplanner.model.UpdateError.UpdateErrorType.NO_UPDATES;
import static org.opentripplanner.model.UpdateError.UpdateErrorType.TRIP_NOT_FOUND_IN_PATTERN;
import static org.opentripplanner.model.UpdateError.UpdateErrorType.UNKNOWN;
import static org.opentripplanner.model.UpdateSuccess.WarningType.NOT_MONITORED;

import com.google.common.base.Preconditions;
import java.time.LocalDate;
Expand All @@ -27,6 +28,7 @@
import org.opentripplanner.model.TimetableSnapshot;
import org.opentripplanner.model.TimetableSnapshotProvider;
import org.opentripplanner.model.UpdateError;
import org.opentripplanner.model.UpdateSuccess;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.framework.Result;
Expand All @@ -43,7 +45,7 @@
import org.opentripplanner.transit.service.TransitModel;
import org.opentripplanner.transit.service.TransitService;
import org.opentripplanner.updater.TimetableSnapshotSourceParameters;
import org.opentripplanner.updater.trip.UpdateResult;
import org.opentripplanner.updater.UpdateResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.org.siri.siri20.DatedVehicleJourneyRef;
Expand Down Expand Up @@ -263,7 +265,7 @@ public UpdateResult applyEstimatedTimetable(
// Acquire lock on buffer
bufferLock.lock();

List<Result<?, UpdateError>> results = new ArrayList<>();
List<Result<UpdateSuccess, UpdateError>> results = new ArrayList<>();

try {
if (fullDataset) {
Expand Down Expand Up @@ -295,13 +297,13 @@ public UpdateResult applyEstimatedTimetable(
return UpdateResult.ofResults(results);
}

private List<Result<?, UpdateError>> apply(
private List<Result<UpdateSuccess, UpdateError>> apply(
EstimatedTimetableDeliveryStructure etDelivery,
TransitModel transitModel,
String feedId,
SiriFuzzyTripMatcher fuzzyTripMatcher
) {
List<Result<?, UpdateError>> results = new ArrayList<>();
List<Result<UpdateSuccess, UpdateError>> results = new ArrayList<>();
List<EstimatedVersionFrameStructure> estimatedJourneyVersions = etDelivery.getEstimatedJourneyVersionFrames();
if (estimatedJourneyVersions != null) {
//Handle deliveries
Expand All @@ -312,7 +314,11 @@ private List<Result<?, UpdateError>> apply(
if (isReplacementDeparture(journey, feedId)) {
// Added trip
try {
Result<?, UpdateError> res = handleAddedTrip(transitModel, feedId, journey);
Result<UpdateSuccess, UpdateError> res = handleAddedTrip(
transitModel,
feedId,
journey
);
results.add(res);
} catch (Throwable t) {
// Since this is work in progress - catch everything to continue processing updates
Expand All @@ -326,15 +332,14 @@ private List<Result<?, UpdateError>> apply(
} else {
// Updated trip
var result = handleModifiedTrip(transitModel, fuzzyTripMatcher, feedId, journey);
result.ifSuccess(ignored -> results.add(Result.success()));
// need to put it in a new instance so the type is correct
result.ifSuccess(value -> results.add(Result.success(value)));
result.ifFailure(failures -> {
failures.stream().map(Result::failure).forEach(results::add);

if (journey.isMonitored() != null && !journey.isMonitored()) {
results.add(
Result.success(UpdateError.noTripId(UpdateError.UpdateErrorType.NOT_MONITORED))
);
}
List<Result<UpdateSuccess, UpdateError>> f = failures
.stream()
.map(Result::<UpdateSuccess, UpdateError>failure)
.toList();
results.addAll(f);
});
}
}
Expand Down Expand Up @@ -501,7 +506,7 @@ private Timetable getCurrentTimetable(TripPattern tripPattern, LocalDate service
return tripPattern.getScheduledTimetable();
}

private Result<?, UpdateError> handleAddedTrip(
private Result<UpdateSuccess, UpdateError> handleAddedTrip(
TransitModel transitModel,
String feedId,
EstimatedVehicleJourney estimatedVehicleJourney
Expand Down Expand Up @@ -574,7 +579,8 @@ private Result<?, UpdateError> handleAddedTrip(
);

if (tripResult.isFailure()) {
return tripResult;
// need to create a new result so the success type is correct
return tripResult.toFailureResult();
}
Trip trip = tripResult.successValue();

Expand Down Expand Up @@ -866,7 +872,7 @@ private Route getReplacedRoute(TransitModel transitModel, String feedId, String
: null;
}

private Result<?, List<UpdateError>> handleModifiedTrip(
private Result<UpdateSuccess, List<UpdateError>> handleModifiedTrip(
TransitModel transitModel,
SiriFuzzyTripMatcher fuzzyTripMatcher,
String feedId,
Expand All @@ -879,7 +885,7 @@ private Result<?, List<UpdateError>> handleModifiedTrip(
estimatedVehicleJourney.isCancellation() != null &&
!estimatedVehicleJourney.isCancellation()
) {
return Result.success();
return Result.success(UpdateSuccess.ofWarnings(NOT_MONITORED));
}
}

Expand All @@ -901,8 +907,11 @@ private Result<?, List<UpdateError>> handleModifiedTrip(

LocalDate serviceDate = getServiceDateForEstimatedVehicleJourney(estimatedVehicleJourney);

final Result<UpdateSuccess, List<UpdateError>> successNoWarnings = Result.success(
UpdateSuccess.noWarnings()
);
if (serviceDate == null) {
return Result.success();
return successNoWarnings;
}

Set<TripTimes> times = new HashSet<>();
Expand Down Expand Up @@ -1058,7 +1067,7 @@ private Result<?, List<UpdateError>> handleModifiedTrip(
}

if (errors.isEmpty()) {
return Result.success();
return successNoWarnings;
} else {
return Result.failure(errors);
}
Expand Down Expand Up @@ -1112,7 +1121,7 @@ private int calculateSecondsSinceMidnight(ZonedDateTime startOfService, ZonedDat
/**
* Add a (new) trip to the transitModel and the buffer
*/
private Result<?, UpdateError> addTripToGraphAndBuffer(
private Result<UpdateSuccess, UpdateError> addTripToGraphAndBuffer(
final String feedId,
final TransitModel transitModel,
final Trip trip,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.TransitModel;
import org.opentripplanner.updater.GraphUpdater;
import org.opentripplanner.updater.UpdateResult;
import org.opentripplanner.updater.WriteToGraphCallback;
import org.opentripplanner.updater.trip.UpdateResult;
import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.TransitModel;
import org.opentripplanner.updater.PollingGraphUpdater;
import org.opentripplanner.updater.UpdateResult;
import org.opentripplanner.updater.WriteToGraphCallback;
import org.opentripplanner.updater.trip.UpdateResult;
import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource;
import org.opentripplanner.framework.io.HttpUtils;
import org.opentripplanner.transit.service.TransitModel;
import org.opentripplanner.updater.trip.UpdateResult;
import org.opentripplanner.updater.UpdateResult;
import org.opentripplanner.updater.trip.metrics.TripUpdateMetrics;
import org.rutebanken.siri20.util.SiriXml;
import org.slf4j.Logger;
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/org/opentripplanner/framework/io/HttpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ public static InputStream getData(URI uri, Map<String, String> requestHeaderValu
return getData(uri, DEFAULT_TIMEOUT, requestHeaderValues);
}

public static List<Header> getHeaders(URI uri) {
return getHeaders(uri, DEFAULT_TIMEOUT, null);
}

public static List<Header> getHeaders(
URI uri,
Duration timeout,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ public Set<NearbyStop> findNearbyStopsConsideringPatterns(
}
}
}

if (OTPFeature.FlexRouting.isOn()) {
for (FlexTrip<?, ?> trip : transitService.getFlexIndex().getFlexTripsByStop(ts1)) {
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public class PickDropMapper {

static PickDrop map(int gtfsCode) {
public static PickDrop map(int gtfsCode) {
return switch (gtfsCode) {
case 0 -> PickDrop.SCHEDULED;
case 1 -> PickDrop.NONE;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/opentripplanner/model/Timetable.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public TripTimes setTripTimes(int tripIndex, TripTimes tt) {
* @param updateServiceDate service date of trip update
* @param backwardsDelayPropagationType Defines when delays are propagated to previous stops and
* if these stops are given the NO_DATA flag
* @return {@link Result<TripTimesPatch, UpdateError>} contains either a new copy of updated
* @return {@link Result<TripTimesPatch, UpdateError >} contains either a new copy of updated
* TripTimes after TripUpdate has been applied on TripTimes of trip with the id specified in the
* trip descriptor of the TripUpdate and a list of stop indices that have been skipped with the
* realtime update; or an error if something went wrong
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public boolean hasRealtimeAddedTripPatterns() {
* @param serviceDate service day for which this update is valid
* @return whether the update was actually applied
*/
public Result<?, UpdateError> update(
public Result<UpdateSuccess, UpdateError> update(
TripPattern pattern,
TripTimes updatedTripTimes,
LocalDate serviceDate
Expand Down Expand Up @@ -235,7 +235,7 @@ public Result<?, UpdateError> update(

// The time tables are finished during the commit

return Result.success();
return Result.success(UpdateSuccess.noWarnings());
}

/**
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/org/opentripplanner/model/UpdateError.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.framework.Result;

/**
* Detailed information about a failure to apply a realtime update, for example for trips or vehicle
* positions.
*/
public record UpdateError(
@Nullable FeedScopedId tripId,
UpdateErrorType errorType,
Expand Down Expand Up @@ -43,7 +47,6 @@ public enum UpdateErrorType {
INVALID_STOP_SEQUENCE,
NOT_IMPLEMENTED_UNSCHEDULED,
NOT_IMPLEMENTED_DUPLICATED,
NOT_MONITORED,
}

public static <T> Result<T, UpdateError> result(FeedScopedId tripId, UpdateErrorType errorType) {
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/org/opentripplanner/model/UpdateSuccess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.opentripplanner.model;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.opentripplanner.framework.collection.ListUtils;

/**
* The result of a successful application of a realtime update, for example for trips or
* vehicle positions. Its only extra information is a collection of possible warnings that
* ought to be looked at but didn't prevent the application of the update.
*/
public record UpdateSuccess(List<WarningType> warnings) {
/**
* Create an instance with no warnings.
*/
public static UpdateSuccess noWarnings() {
return new UpdateSuccess(List.of());
}
/**
* Create an instance with the provided warnings.
*/
public static UpdateSuccess ofWarnings(WarningType... warnings) {
return new UpdateSuccess(Arrays.asList(warnings));
}

/**
* Return a copy of the instance with the provided warnings added.
*/
public UpdateSuccess addWarnings(Collection<WarningType> addedWarnings) {
return new UpdateSuccess(ListUtils.combine(this.warnings, addedWarnings));
}

public enum WarningType {
/**
* An added trip contained references to stops that are not in the static data. These
* stops have been removed.
*/
UNKNOWN_STOPS_REMOVED_FROM_ADDED_TRIP,
NOT_MONITORED,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nonnull;

/**
Expand All @@ -25,6 +26,32 @@ public static <E> Result<Void, E> success() {
return new Success<>(null);
}

/**
* If this instance is a success then the mapper transforms its value. If this instance is a
* failure then a new failed instance with the correct success type is returned.
*
* @param <N> The success type of the new Result instance.
*/
public <N> Result<N, E> mapSuccess(Function<T, N> mapper) {
if (isSuccess()) {
return Result.success(mapper.apply(successValue()));
} else {
return Result.failure(failureValue());
}
}

/**
* Creates a new instance of this class with a new success type. This is useful if you know
* that it is a failure and want to return it in a method without having to cast the success type.
* <p>
* If this instance is not a failure an exception is thrown.
*
* @param <N> The success type of the new Result instance.
*/
public <N> Result<N, E> toFailureResult() {
return Result.failure(failureValue());
}

/**
* Get the value contained with an erased type. If you want to use the typed version of the value
* use {@link Result#ifFailure(Consumer)} or {@link Result#ifSuccess(Consumer)}}.
Expand Down
Loading

0 comments on commit 37489ac

Please sign in to comment.