Skip to content
This repository has been archived by the owner on May 27, 2024. It is now read-only.

Fix #335 Check that delay is not set for freq type 0 trips #337

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
21 changes: 21 additions & 0 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Rules are declared in the [`ValidationRules` class](https://github.com/CUTR-at-U
| [E050](#E050) | `timestamp` is in the future
| [E051](#E051) | GTFS-rt `stop_sequence` not found in GTFS data
| [E052](#E052) | `vehicle.id` is not unique
| [E053](#E053) | `start_time` for trip has changed
| [E054](#E054) | `delay` for freq based trip with exact times=0 is set

### Table of Warnings

Expand Down Expand Up @@ -717,6 +719,25 @@ From [VehiclePosition.VehicleDescriptor](https://github.com/google/transit/blob/
#### References:
* [`vehicle.id`](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-vehicledescriptor)

<a name="E053"/>

### E053 - `start_time` for trip has changed

A frequency based trip should have the same start time in the descriptor.

From [TripUpdate.TripDescriptor](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-tripdescriptor) for `start_time`:

>If the trip corresponds to exact_times=0, then its start_time may be arbitrary, and is initially expected to be the first departure of the trip. Once established, the start_time of this frequency-based exact_times=0 trip should be considered immutable, even if the first departure time changes

### E054 - `start_time` for trip has changed

A frequency based trip of type 0 should not have any delay values set.

From [TripUpdate](https://github.com/google/transit/blob/master/gtfs-realtime/spec/en/reference.md#message-tripupdate) for `delay`:

>Delay should only be specified when the prediction is given relative to some existing schedule in GTFS.


# Warnings

<a name="W001"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,14 @@ public class ValidationRules {
public static final ValidationRule E052 = new ValidationRule("E052", "ERROR", "vehicle.id is not unique",
"Each vehicle should have a unique ID",
"which is used by more than one vehicle in the feed");

public static final ValidationRule E053 = new ValidationRule("E053", "ERROR", "start_time for frequency-based trip changed.",
"start_time for frequency-based trips",
"exact_times=0 must be immutable");

public static final ValidationRule E054 = new ValidationRule("E054", "ERROR", "Frequency type 0 trips should not have delay set.",
"Frequency type 0 trips should",
"not have delay values");

private static List<ValidationRule> mAllRules = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package edu.usf.cutr.gtfsrtvalidator.lib.validation.rules;

import com.google.transit.realtime.GtfsRealtime;
import com.google.transit.realtime.GtfsRealtime.TripUpdate;
import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate;

import edu.usf.cutr.gtfsrtvalidator.lib.model.MessageLogModel;
import edu.usf.cutr.gtfsrtvalidator.lib.model.OccurrenceModel;
import edu.usf.cutr.gtfsrtvalidator.lib.model.helper.ErrorListHelperModel;
Expand All @@ -27,6 +30,7 @@
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static edu.usf.cutr.gtfsrtvalidator.lib.validation.ValidationRules.*;
Expand All @@ -41,17 +45,22 @@
public class FrequencyTypeZeroValidator implements FeedEntityValidator {

private static final org.slf4j.Logger _log = LoggerFactory.getLogger(FrequencyTypeZeroValidator.class);




@Override
public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsMutableDao gtfsData, GtfsMetadata gtfsMetadata, GtfsRealtime.FeedMessage feedMessage, GtfsRealtime.FeedMessage previousFeedMessage, GtfsRealtime.FeedMessage combinedFeedMessage) {
List<OccurrenceModel> errorListE006 = new ArrayList<>();
List<OccurrenceModel> errorListE013 = new ArrayList<>();
List<OccurrenceModel> errorListW005 = new ArrayList<>();
List<OccurrenceModel> errorListE053 = new ArrayList<>();
List<OccurrenceModel> errorListE054 = new ArrayList<>();

for (GtfsRealtime.FeedEntity entity : feedMessage.getEntityList()) {
if (entity.hasTripUpdate()) {

GtfsRealtime.TripUpdate tripUpdate = entity.getTripUpdate();

if (gtfsMetadata.getExactTimesZeroTripIds().contains(tripUpdate.getTrip().getTripId())) {
/**
* NOTE - W006 checks for missing trip_ids, because we can't check for that here - we need the trip_id to know if it's exact_times=0
Expand All @@ -75,9 +84,53 @@ public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsMutableDa
// W005 - Missing vehicle_id in trip_update for frequency-based exact_times = 0
RuleUtils.addOccurrence(W005, "trip_id " + tripUpdate.getTrip().getTripId(), errorListW005, _log);
}

if(previousFeedMessage!=null)
{
for (GtfsRealtime.FeedEntity previousEntity : previousFeedMessage.getEntityList())
{
if (previousEntity.hasTripUpdate())
{
if(tripUpdate.getVehicle()!=null && previousEntity.getTripUpdate().getVehicle()!=null)
{
if(previousEntity.getTripUpdate().getVehicle().getId().equals(tripUpdate.getVehicle().getId()))
{
if(tripUpdate.getStopTimeUpdateCount()>0 && previousEntity.getTripUpdate().getStopTimeUpdateCount()>0)
{
if(tripUpdate.getStopTimeUpdate(tripUpdate.getStopTimeUpdateCount()-1).getStopSequence()>=previousEntity.getTripUpdate().getStopTimeUpdate(previousEntity.getTripUpdate().getStopTimeUpdateCount()-1).getStopSequence())
{
// E053 - start time of trip not consistent for trip updates for frequency-based exact_times = 0
if(!tripUpdate.getTrip().getStartTime().equals(previousEntity.getTripUpdate().getTrip().getStartTime()))
{
RuleUtils.addOccurrence(E053, "vehicle_id " + tripUpdate.getVehicle().getId(), errorListE053, _log);
}
}
}
}
}
}
}
}

for(StopTimeUpdate stopTimeUpdate:tripUpdate.getStopTimeUpdateList())
{
if(stopTimeUpdate.hasArrival())
{
if(stopTimeUpdate.getArrival().hasDelay())
{
RuleUtils.addOccurrence(E054, "vehicle_id " + tripUpdate.getVehicle().getId(), errorListE054, _log);
}
}
if(stopTimeUpdate.hasDeparture())
{
if(stopTimeUpdate.getDeparture().hasDelay())
{
RuleUtils.addOccurrence(E054, "vehicle_id " + tripUpdate.getVehicle().getId(), errorListE054, _log);
}
}
}
}
}

if (entity.hasVehicle()) {
GtfsRealtime.VehiclePosition vehiclePosition = entity.getVehicle();
if (vehiclePosition.hasTrip() &&
Expand Down Expand Up @@ -119,6 +172,12 @@ public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsMutableDa
if (!errorListW005.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(W005), errorListW005));
}
if (!errorListE053.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E053), errorListE053));
}
if (!errorListE054.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E054), errorListE054));
}
return errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,6 @@ public void testIsInFuture() {
@Test
public void testGetAllRules() {
List<ValidationRule> rules = ValidationRules.getRules();
assertEquals(61, rules.size());
assertEquals(63, rules.size());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package edu.usf.cutr.gtfsrtvalidator.lib.test.rules;

import com.google.transit.realtime.GtfsRealtime;
import com.google.transit.realtime.GtfsRealtime.FeedMessage;

import edu.usf.cutr.gtfsrtvalidator.lib.model.ValidationRule;
import edu.usf.cutr.gtfsrtvalidator.lib.test.FeedMessageTest;
import edu.usf.cutr.gtfsrtvalidator.lib.test.util.TestUtils;
Expand Down Expand Up @@ -262,4 +264,136 @@ public void testW005() {

clearAndInitRequiredFeedFields();
}
/**
* E053- inconsistent start time in trip descriptor for frequency-based exact_times = 0
*/
@Test
public void testE053()
{
FrequencyTypeZeroValidator frequencyTypeZeroValidator = new FrequencyTypeZeroValidator();
Map<ValidationRule, Integer> expected = new HashMap<>();
FeedMessage[] feedMessages= new FeedMessage[2];
for( int i = 0; i <2;i++)
{
GtfsRealtime.TripDescriptor.Builder tripDescriptorBuilder = GtfsRealtime.TripDescriptor.newBuilder();

tripDescriptorBuilder.setTripId("1");
tripDescriptorBuilder.setStartDate("4-24-2016");
if(i==0)
{
tripDescriptorBuilder.setStartTime("08:00:00AM");
}
if(i==1)
{
tripDescriptorBuilder.setStartTime("09:00:00AM");
}


GtfsRealtime.VehicleDescriptor.Builder vehicleDescriptorBuilder = GtfsRealtime.VehicleDescriptor.newBuilder();

vehicleDescriptorBuilder.setId("1");

vehiclePositionBuilder.setVehicle(vehicleDescriptorBuilder.build());
vehiclePositionBuilder.setTimestamp(TimestampUtils.MIN_POSIX_TIME);
vehiclePositionBuilder.setTrip(tripDescriptorBuilder.build());
vehiclePositionBuilder.setVehicle(vehicleDescriptorBuilder.build());

feedEntityBuilder.setVehicle(vehiclePositionBuilder.build());

feedMessageBuilder.setEntity(0, feedEntityBuilder.build());

tripUpdateBuilder.setVehicle(vehicleDescriptorBuilder.build());
tripUpdateBuilder.setTrip(tripDescriptorBuilder.build());

for(int j=0;j<i+1;j++)
{
GtfsRealtime.TripUpdate.StopTimeUpdate.Builder stopTimeUpdateBuilder = GtfsRealtime.TripUpdate.StopTimeUpdate.newBuilder();

stopTimeUpdateBuilder.setStopId(""+j);
stopTimeUpdateBuilder.setStopSequence(j);

GtfsRealtime.TripUpdate.StopTimeEvent.Builder stopTimeEventBuilder = GtfsRealtime.TripUpdate.StopTimeEvent.newBuilder();
stopTimeUpdateBuilder.setArrival(stopTimeEventBuilder.build());

tripUpdateBuilder.addStopTimeUpdate(stopTimeUpdateBuilder.build());
}

feedEntityBuilder.setTripUpdate(tripUpdateBuilder.build());

// Add vehicle_id to vehicle position - 1 warning
feedEntityBuilder.setVehicle(vehiclePositionBuilder.build());

feedMessageBuilder.setEntity(0, feedEntityBuilder.build());

feedMessages[i]=feedMessageBuilder.build();


}
results = frequencyTypeZeroValidator.validate(TimestampUtils.MIN_POSIX_TIME, bullRunnerGtfs, bullRunnerGtfsMetadata, feedMessages[1], feedMessages[0], null);
expected.put(ValidationRules.E053, 1);
TestUtils.assertResults(expected, results);
expected.clear();
clearAndInitRequiredFeedFields();
}
/**
* E054- delay should not be set for frequency-based exact_times = 0
*/
@Test
public void testE054()
{
FrequencyTypeZeroValidator frequencyTypeZeroValidator = new FrequencyTypeZeroValidator();
Map<ValidationRule, Integer> expected = new HashMap<>();
FeedMessage[] feedMessages= new FeedMessage[2];

GtfsRealtime.TripDescriptor.Builder tripDescriptorBuilder = GtfsRealtime.TripDescriptor.newBuilder();

tripDescriptorBuilder.setTripId("1");
tripDescriptorBuilder.setStartDate("4-24-2016");

tripDescriptorBuilder.setStartTime("08:00:00AM");


GtfsRealtime.VehicleDescriptor.Builder vehicleDescriptorBuilder = GtfsRealtime.VehicleDescriptor.newBuilder();

vehicleDescriptorBuilder.setId("1");

vehiclePositionBuilder.setVehicle(vehicleDescriptorBuilder.build());
vehiclePositionBuilder.setTimestamp(TimestampUtils.MIN_POSIX_TIME);
vehiclePositionBuilder.setTrip(tripDescriptorBuilder.build());
vehiclePositionBuilder.setVehicle(vehicleDescriptorBuilder.build());

feedEntityBuilder.setVehicle(vehiclePositionBuilder.build());

feedMessageBuilder.setEntity(0, feedEntityBuilder.build());

tripUpdateBuilder.setVehicle(vehicleDescriptorBuilder.build());
tripUpdateBuilder.setTrip(tripDescriptorBuilder.build());


GtfsRealtime.TripUpdate.StopTimeUpdate.Builder stopTimeUpdateBuilder = GtfsRealtime.TripUpdate.StopTimeUpdate.newBuilder();

stopTimeUpdateBuilder.setStopId(""+1);
stopTimeUpdateBuilder.setStopSequence(1);


GtfsRealtime.TripUpdate.StopTimeEvent.Builder stopTimeEventBuilder = GtfsRealtime.TripUpdate.StopTimeEvent.newBuilder();
stopTimeEventBuilder.setDelay(10);
stopTimeUpdateBuilder.setArrival(stopTimeEventBuilder.build());

tripUpdateBuilder.addStopTimeUpdate(stopTimeUpdateBuilder.build());


feedEntityBuilder.setTripUpdate(tripUpdateBuilder.build());


feedEntityBuilder.setVehicle(vehiclePositionBuilder.build());

feedMessageBuilder.setEntity(0, feedEntityBuilder.build());

results = frequencyTypeZeroValidator.validate(TimestampUtils.MIN_POSIX_TIME, bullRunnerGtfs, bullRunnerGtfsMetadata, feedMessageBuilder.build(), null, null);
expected.put(ValidationRules.E054, 1);
TestUtils.assertResults(expected, results);
expected.clear();
clearAndInitRequiredFeedFields();
}
}