Skip to content

Commit 1a3ed74

Browse files
authored
Merge pull request opentripplanner#5585 from opentripplanner/abyrd/realtime-javadoc
Add and update Javadoc on realtime classes
2 parents 2562fb5 + df3361f commit 1a3ed74

File tree

48 files changed

+5804
-242
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+5804
-242
lines changed

src/ext/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandler.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,36 @@
3535
import uk.org.siri.siri20.WorkflowStatusEnumeration;
3636

3737
/**
38-
* This updater applies the equivalent of GTFS Alerts, but from SIRI Situation Exchange feeds. NOTE
39-
* this cannot handle situations where there are multiple feeds with different IDs (for now it may
40-
* only work in single-feed regions).
38+
* This updater applies the equivalent of GTFS Alerts, but from SIRI Situation Exchange (SX) feeds.
39+
* As the incoming SIRI SX messages are mapped to internal TransitAlerts, their FeedScopedIds will
40+
* be the single feed ID associated with this update handler, plus the situation number provided in
41+
* the SIRI SX message.
42+
* This class cannot handle situations where incoming messages are being applied to multiple static
43+
* feeds with different IDs. For now it may only work in single-feed regions. A possible workaround
44+
* is to assign the same feed ID to multiple static feeds where it is known that their entity IDs
45+
* are all drawn from the same namespace (i.e. they are functionally fragments of the same feed).
46+
* TODO RT_AB: Internal FeedScopedId creation strategy should probably be pluggable or configurable.
47+
* TG has indicated this is a necessary condition for moving this updater out of sandbox.
48+
* TODO RT_AB: The name should be clarified, as there is no such thing as "SIRI Alerts", and it
49+
* is referencing the internal model concept of "Alerts" which are derived from GTFS terminology.
4150
*/
4251
public class SiriAlertsUpdateHandler {
4352

4453
private static final Logger LOG = LoggerFactory.getLogger(SiriAlertsUpdateHandler.class);
4554
private final String feedId;
4655
private final Set<TransitAlert> alerts = new HashSet<>();
4756
private final TransitAlertService transitAlertService;
48-
/** How long before the posted start of an event it should be displayed to users */
4957
private final Duration earlyStart;
58+
59+
/**
60+
* This takes the parts of the SIRI SX message saying which transit entities are affected and
61+
* maps them to the corresponding OTP internal model entity or entities.
62+
*/
5063
private final AffectsMapper affectsMapper;
5164

65+
/**
66+
* @param earlyStart display the alerts to users this long before their activePeriod begins
67+
*/
5268
public SiriAlertsUpdateHandler(
5369
String feedId,
5470
TransitModel transitModel,
@@ -90,7 +106,7 @@ public void update(ServiceDelivery delivery) {
90106
} else {
91107
TransitAlert alert = null;
92108
try {
93-
alert = handleAlert(sxElement);
109+
alert = mapSituationToAlert(sxElement);
94110
addedCounter++;
95111
} catch (Exception e) {
96112
LOG.info(
@@ -120,7 +136,12 @@ public void update(ServiceDelivery delivery) {
120136
}
121137
}
122138

123-
private TransitAlert handleAlert(PtSituationElement situation) {
139+
/**
140+
* Build an internal model Alert from an incoming SIRI situation exchange element.
141+
* May return null if the header, description, and detail text are all empty or missing in the
142+
* SIRI message. In all other cases it will return a valid TransitAlert instance.
143+
*/
144+
private TransitAlert mapSituationToAlert(PtSituationElement situation) {
124145
TransitAlertBuilder alert = createAlertWithTexts(situation);
125146

126147
if (
@@ -199,7 +220,10 @@ private long getEpochSecond(ZonedDateTime startTime) {
199220
}
200221

201222
/*
202-
* Creates alert from PtSituation with all textual content
223+
* Creates a builder for an internal model TransitAlert. The builder is pre-filled with all
224+
* textual content from the supplied SIRI PtSituation. The builder also has the feed scoped ID
225+
* pre-set to the single feed ID associated with this update handler, plus the situation number
226+
* provided in the SIRI PtSituation.
203227
*/
204228
private TransitAlertBuilder createAlertWithTexts(PtSituationElement situation) {
205229
return TransitAlert

src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,12 @@ public TimetableSnapshot getTimetableSnapshot() {
133133

134134
/**
135135
* Method to apply a trip update list to the most recent version of the timetable snapshot.
136+
* FIXME RT_AB: TripUpdate is the GTFS term, and these SIRI ETs are never converted into that
137+
* same internal model.
136138
*
137139
* @param fullDataset true iff the list with updates represent all updates that are active right
138140
* now, i.e. all previous updates should be disregarded
139-
* @param updates SIRI VehicleMonitoringDeliveries that should be applied atomically
141+
* @param updates SIRI EstimatedTimetable deliveries that should be applied atomically.
140142
*/
141143
public UpdateResult applyEstimatedTimetable(
142144
@Nullable SiriFuzzyTripMatcher fuzzyTripMatcher,
@@ -372,12 +374,9 @@ private Result<UpdateSuccess, UpdateError> addTripToGraphAndBuffer(TripUpdate tr
372374
trip,
373375
serviceDate
374376
);
375-
376-
// Add new trip times to the buffer and return success
377+
// Add new trip times to buffer, making protective copies as needed. Bubble success/error up.
377378
var result = buffer.update(pattern, tripUpdate.tripTimes(), serviceDate);
378-
379379
LOG.debug("Applied real-time data for trip {} on {}", trip, serviceDate);
380-
381380
return result;
382381
}
383382

src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java

Lines changed: 110 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,63 @@
1212
import javax.annotation.Nonnull;
1313
import org.opentripplanner.transit.model.network.StopPattern;
1414
import org.opentripplanner.transit.model.network.TripPattern;
15-
import org.opentripplanner.transit.model.network.TripPatternBuilder;
1615
import org.opentripplanner.transit.model.site.RegularStop;
1716
import org.opentripplanner.transit.model.site.StopLocation;
1817
import org.opentripplanner.transit.model.timetable.Trip;
1918
import org.slf4j.Logger;
2019
import org.slf4j.LoggerFactory;
2120

2221
/**
23-
* A synchronized cache of trip patterns that are added to the graph due to GTFS-realtime messages.
22+
* Threadsafe mechanism for tracking any TripPatterns added to the graph via SIRI realtime messages.
23+
* This tracks only patterns added by realtime messages, not ones that already existed from the
24+
* scheduled NeTEx. This is a "cache" in the sense that it will keep returning the same TripPattern
25+
* when presented with the same StopPattern, so if realtime messages add many trips passing through
26+
* the same sequence of stops, they will all end up on this same TripPattern.
27+
* <p>
28+
* Note that there are two versions of this class, this one for GTFS-RT and another for SIRI.
29+
* See additional comments in the Javadoc of the GTFS-RT version of this class, whose name is
30+
* simply TripPatternCache.
31+
* TODO RT_AB: To the extent that double SIRI/GTFS implementations are kept, prefix all names
32+
* with GTFS or SIRI or NETEX rather than having no prefix on the GTFS versions.
33+
* TODO RT_TG: There is no clear strategy for what should be in the cache and the transit model and the flow
34+
* between them. The NeTEx and a GTFS version of this should be merged. Having NeTex and GTFS
35+
* specific indexes inside is ok. With the increased usage of DatedServiceJourneys, this should probably
36+
* be part of the main model - not a separate cashe. It is possible that this class works when it comes to
37+
* the thread-safety, but just by looking at a few lines of code I see problems - a strategy needs to be
38+
* analysed, designed and documented.
2439
*/
2540
public class SiriTripPatternCache {
2641

27-
private static final Logger log = LoggerFactory.getLogger(SiriTripPatternCache.class);
42+
private static final Logger LOG = LoggerFactory.getLogger(SiriTripPatternCache.class);
2843

44+
// TODO RT_AB: Improve documentation. This seems to be the primary collection of added
45+
// TripPatterns, with other collections serving as indexes. Similar to TripPatternCache.cache
46+
// in the GTFS version of this class, but with service date as part of the key.
2947
private final Map<StopPatternServiceDateKey, TripPattern> cache = new HashMap<>();
3048

49+
// TODO RT_AB: Improve documentation. This field appears to be an index that exists only in the
50+
// SIRI version of this class (i.e. this version and not the older TripPatternCache that
51+
// handles GTFS-RT). This index appears to be tailored for use by the Transmodel GraphQL APIs.
3152
private final ListMultimap<StopLocation, TripPattern> patternsForStop = Multimaps.synchronizedListMultimap(
3253
ArrayListMultimap.create()
3354
);
3455

56+
// TODO RT_AB: clarify name and add documentation to this field.
3557
private final Map<TripServiceDateKey, TripPattern> updatedTripPatternsForTripCache = new HashMap<>();
3658

59+
// TODO RT_AB: generalize this so we can generate IDs for SIRI or GTFS-RT sources.
3760
private final SiriTripPatternIdGenerator tripPatternIdGenerator;
61+
3862
private final Function<Trip, TripPattern> getPatternForTrip;
3963

64+
/**
65+
* TODO RT_AB: This class could potentially be reused for both SIRI and GTFS-RT, which may
66+
* involve injecting a different ID generator and pattern fetching method.
67+
*
68+
* @param getPatternForTrip SiriTripPatternCache needs only this one feature of TransitService, so we retain
69+
* only this function reference to effectively narrow the interface. This should also facilitate
70+
* testing.
71+
*/
4072
public SiriTripPatternCache(
4173
SiriTripPatternIdGenerator tripPatternIdGenerator,
4274
Function<Trip, TripPattern> getPatternForTrip
@@ -46,8 +78,13 @@ public SiriTripPatternCache(
4678
}
4779

4880
/**
49-
* Get cached trip pattern or create one if it doesn't exist yet. If a trip pattern is created,
50-
* vertices and edges for this trip pattern are also created in the transitModel.
81+
* Get cached trip pattern or create one if it doesn't exist yet.
82+
*
83+
* TODO RT_AB: Improve documentation and/or merge with GTFS version of this class.
84+
* This was clearly derived from a method from TripPatternCache. This is the only non-dead-code
85+
* public method on this class, and mirrors the only public method on the GTFS-RT version of
86+
* TripPatternCache. It also explains why this class is called a "cache". It allows reusing the
87+
* same TripPattern instance when many different trips are created or updated with the same pattern.
5188
*
5289
* @param stopPattern stop pattern to retrieve/create trip pattern
5390
* @param trip Trip containing route of new trip pattern in case a new trip pattern will be
@@ -61,6 +98,9 @@ public synchronized TripPattern getOrCreateTripPattern(
6198
) {
6299
TripPattern originalTripPattern = getPatternForTrip.apply(trip);
63100

101+
// TODO RT_AB: Verify implementation, which is different than the GTFS-RT version.
102+
// It can return a TripPattern from the scheduled data, but protective copies are handled in
103+
// TimetableSnapshot.update. Better document this aspect of the contract in this method's Javadoc.
64104
if (originalTripPattern.getStopPattern().equals(stopPattern)) {
65105
return originalTripPattern;
66106
}
@@ -72,56 +112,57 @@ public synchronized TripPattern getOrCreateTripPattern(
72112
// Create TripPattern if it doesn't exist yet
73113
if (tripPattern == null) {
74114
var id = tripPatternIdGenerator.generateUniqueTripPatternId(trip);
75-
TripPatternBuilder tripPatternBuilder = TripPattern
76-
.of(id)
77-
.withRoute(trip.getRoute())
78-
.withMode(trip.getMode())
79-
.withNetexSubmode(trip.getNetexSubMode())
80-
.withStopPattern(stopPattern);
81-
82-
// TODO - SIRI: Add pattern to transitModel index?
83-
84-
tripPatternBuilder.withCreatedByRealtimeUpdater(true);
85-
tripPatternBuilder.withOriginalTripPattern(originalTripPattern);
86-
87-
tripPattern = tripPatternBuilder.build();
115+
tripPattern =
116+
TripPattern
117+
.of(id)
118+
.withRoute(trip.getRoute())
119+
.withMode(trip.getMode())
120+
.withNetexSubmode(trip.getNetexSubMode())
121+
.withStopPattern(stopPattern)
122+
.withCreatedByRealtimeUpdater(true)
123+
.withOriginalTripPattern(originalTripPattern)
124+
.build();
125+
// TODO: Add pattern to transitModel index?
88126

89127
// Add pattern to cache
90128
cache.put(key, tripPattern);
91129
}
92130

93-
/**
94-
*
95-
* When the StopPattern is first modified (e.g. change of platform), then updated (or vice versa), the stopPattern is altered, and
96-
* the StopPattern-object for the different states will not be equal.
97-
*
98-
* This causes both tripPatterns to be added to all unchanged stops along the route, which again causes duplicate results
99-
* in departureRow-searches (one departure for "updated", one for "modified").
100-
*
101-
* Full example:
102-
* Planned stops: Stop 1 - Platform 1, Stop 2 - Platform 1
103-
*
104-
* StopPattern #rt1: "updated" stopPattern cached in 'patternsForStop':
105-
* - Stop 1, Platform 1
106-
* - StopPattern #rt1
107-
* - Stop 2, Platform 1
108-
* - StopPattern #rt1
109-
*
110-
* "modified" stopPattern: Stop 1 - Platform 1, Stop 2 - Platform 2
111-
*
112-
* StopPattern #rt2: "modified" stopPattern cached in 'patternsForStop' will then be:
113-
* - Stop 1, Platform 1
114-
* - StopPattern #rt1, StopPattern #rt2
115-
* - Stop 2, Platform 1
116-
* - StopPattern #rt1
117-
* - Stop 2, Platform 2
118-
* - StopPattern #rt2
119-
*
120-
*
121-
* Therefore, we must cleanup the duplicates by deleting the previously added (and thus outdated)
122-
* tripPattern for all affected stops. In example above, "StopPattern #rt1" should be removed from all stops
123-
*
124-
*/
131+
/*
132+
When the StopPattern is first modified (e.g. change of platform), then updated (or vice
133+
versa), the stopPattern is altered, and the StopPattern-object for the different states will
134+
not be equal.
135+
136+
This causes both tripPatterns to be added to all unchanged stops along the route, which again
137+
causes duplicate results in departureRow-searches (one departure for "updated", one for
138+
"modified").
139+
140+
Full example:
141+
Planned stops: Stop 1 - Platform 1, Stop 2 - Platform 1
142+
143+
StopPattern #rt1: "updated" stopPattern cached in 'patternsForStop':
144+
- Stop 1, Platform 1
145+
- StopPattern #rt1
146+
- Stop 2, Platform 1
147+
- StopPattern #rt1
148+
149+
"modified" stopPattern: Stop 1 - Platform 1, Stop 2 - Platform 2
150+
151+
StopPattern #rt2: "modified" stopPattern cached in 'patternsForStop' will then be:
152+
- Stop 1, Platform 1
153+
- StopPattern #rt1, StopPattern #rt2
154+
- Stop 2, Platform 1
155+
- StopPattern #rt1
156+
- Stop 2, Platform 2
157+
- StopPattern #rt2
158+
159+
Therefore, we must clean up the duplicates by deleting the previously added (and thus
160+
outdated) tripPattern for all affected stops. In example above, "StopPattern #rt1" should be
161+
removed from all stops.
162+
163+
TODO RT_AB: review why this particular case is handled in an ad-hoc manner. It seems like all
164+
such indexes should be constantly rebuilt and versioned along with the TimetableSnapshot.
165+
*/
125166
TripServiceDateKey tripServiceDateKey = new TripServiceDateKey(trip, serviceDate);
126167
if (updatedTripPatternsForTripCache.containsKey(tripServiceDateKey)) {
127168
// Remove previously added TripPatterns for the trip currently being updated - if the stopPattern does not match
@@ -132,16 +173,14 @@ public synchronized TripPattern getOrCreateTripPattern(
132173
patternsForStop.values().removeAll(Arrays.asList(cachedTripPattern));
133174
int sizeAfter = patternsForStop.values().size();
134175

135-
log.debug(
176+
LOG.debug(
136177
"Removed outdated TripPattern for {} stops in {} ms - tripId: {}",
137178
(sizeBefore - sizeAfter),
138179
(System.currentTimeMillis() - t1),
139180
trip.getId()
140181
);
141-
/*
142-
TODO: Also remove previously updated - now outdated - TripPattern from cache ?
143-
cache.remove(new StopPatternServiceDateKey(cachedTripPattern.stopPattern, serviceDate));
144-
*/
182+
// TODO: Also remove previously updated - now outdated - TripPattern from cache ?
183+
// cache.remove(new StopPatternServiceDateKey(cachedTripPattern.stopPattern, serviceDate));
145184
}
146185
}
147186

@@ -160,6 +199,7 @@ public synchronized TripPattern getOrCreateTripPattern(
160199

161200
/**
162201
* Returns any new TripPatterns added by real time information for a given stop.
202+
* TODO RT_AB: this appears to be currently unused. Perhaps remove it if the API has changed.
163203
*
164204
* @param stop the stop
165205
* @return list of TripPatterns created by real time sources for the stop.
@@ -169,6 +209,16 @@ public List<TripPattern> getAddedTripPatternsForStop(RegularStop stop) {
169209
}
170210
}
171211

212+
// TODO RT_AB: move the below classes inside the above class as private static inner classes.
213+
// Defining these additional classes in the same top-level class file is unconventional.
214+
215+
/**
216+
* Serves as the key for the collection of TripPatterns added by realtime messages.
217+
* Must define hashcode and equals to confer semantic identity.
218+
* TODO RT_AB: clarify why each date has a different TripPattern instead of a different Timetable.
219+
* It seems like there's a separate TripPattern instance for each StopPattern and service date,
220+
* rather a single TripPattern instance associated with a separate timetable for each date.
221+
*/
172222
class StopPatternServiceDateKey {
173223

174224
StopPattern stopPattern;
@@ -194,6 +244,11 @@ public boolean equals(Object thatObject) {
194244
}
195245
}
196246

247+
/**
248+
* An alternative key for looking up realtime-added TripPatterns by trip and service date instead
249+
* of stop pattern and service date. Must define hashcode and equals to confer semantic identity.
250+
* TODO RT_AB: verify whether one map is considered the definitive collection and the other an index.
251+
*/
197252
class TripServiceDateKey {
198253

199254
Trip trip;

src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternIdGenerator.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@
88
import org.opentripplanner.transit.model.timetable.Trip;
99

1010
/**
11-
* This class generate a new id for new TripPatterns created real-time by the SIRI updaters. It is
12-
* important to creat only on instance of this class, and inject it where it is needed.
13-
* <p>
14-
* The id generation is thread-safe, even if that is probably not needed.
11+
* This class generates new unique IDs for TripPatterns created in response to real-time updates
12+
* from the SIRI updaters. In non-test usage it is important to create only one instance of this
13+
* class, and inject that single instance wherever it is needed. However, this single-instance
14+
* usage pattern is not enforced due to differing needs in tests.
15+
* The ID generation is threadsafe, even if that is probably not needed.
1516
*/
1617
class SiriTripPatternIdGenerator {
1718

1819
private final AtomicInteger counter = new AtomicInteger(0);
1920

2021
/**
21-
* Generate unique trip pattern code for real-time added trip pattern. This function roughly
22-
* follows the format of {@link GenerateTripPatternsOperation}.
23-
* <p>
24-
* The generator add a postfix 'RT' to indicate that this trip pattern is generated at REAL-TIME.
22+
* Generate a unique ID for a trip pattern added in response to a realtime message. This function
23+
* roughly follows the format of {@link GenerateTripPatternsOperation}. The generator suffixes the
24+
* ID with 'RT' to indicate that this trip pattern is generated in response to a realtime message.
2525
*/
2626
FeedScopedId generateUniqueTripPatternId(Trip trip) {
2727
Route route = trip.getRoute();

src/ext/java/org/opentripplanner/ext/siri/mapper/AffectsMapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333

3434
/**
3535
* Maps a {@link AffectsScopeStructure} to a list of {@link EntitySelector}s
36+
*
37+
* Concretely: this takes the parts of the SIRI SX (Alerts) message describing which transit
38+
* entities are concerned by the alert, and maps them to EntitySelectors, which can match multiple
39+
* OTP internal model entities that should be associated with the message.
3640
*/
3741
public class AffectsMapper {
3842

0 commit comments

Comments
 (0)