use of edu.usf.cutr.gtfsrtvalidator.lib.model.MessageLogModel in project gtfs-realtime-validator by CUTR-at-USF.
the class CrossFeedDescriptorValidator method validate.
@Override
public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsDaoImpl gtfsData, GtfsMetadata gtfsMetadata, GtfsRealtime.FeedMessage feedMessage, GtfsRealtime.FeedMessage previousFeedMessage, GtfsRealtime.FeedMessage combinedFeedMessage) {
if (combinedFeedMessage == null) {
// If only one GTFS-rt feed is being monitored for the GTFS dataset, then don't run any of the cross-feed rules
return new ArrayList<>();
}
List<OccurrenceModel> w003List = new ArrayList<>();
List<OccurrenceModel> e047List = new ArrayList<>();
/*
Create inverse maps, so we can efficiently check if a trip_id in TripUpdates is in VehiclePositions, and if
vehicle_id in VehiclePositions is in TripUpdates.
*/
// key is trip_id from TripUpdates feed, value is vehicle.id
HashMap<String, String> tripUpdatesTripIdToVehicleId = new HashMap<>();
// key is vehicle.id from TripUpdates feed, value is trip_id
HashMap<String, String> tripUpdatesVehicleIdToTripId = new HashMap<>();
// A set of trips (key = trip_id) that don't have any vehicle.ids
Set<String> tripsWithoutVehicles = new HashSet<>();
int tripUpdateCount = 0;
// key is vehicle_id from VehiclePositions feed, value is trip_id
HashMap<String, String> vehiclePositionsVehicleIdToTripId = new HashMap<>();
// key is trip_id from VehiclePositions feed, value is vehicle_id
HashMap<String, String> vehiclePositionsTripIdToVehicleId = new HashMap<>();
// A set of vehicles (key = vehicle.id) that don't have any trip_ids
Set<String> vehiclesWithoutTrips = new HashSet<>();
int vehiclePositionCount = 0;
// Build the maps
for (GtfsRealtime.FeedEntity entity : combinedFeedMessage.getEntityList()) {
if (entity.hasTripUpdate() && hasTripId(entity.getTripUpdate())) {
tripUpdateCount++;
String tripId = entity.getTripUpdate().getTrip().getTripId();
String vehicleId = "";
if (entity.getTripUpdate().hasVehicle() && entity.getTripUpdate().getVehicle().hasId()) {
vehicleId = entity.getTripUpdate().getVehicle().getId();
}
if (StringUtils.isEmpty(vehicleId)) {
// Trip does not have a vehicle.id - add it to the set
tripsWithoutVehicles.add(tripId);
} else {
// Trip has a vehicle.id - add it to the HashMap
tripUpdatesTripIdToVehicleId.put(tripId, vehicleId);
tripUpdatesVehicleIdToTripId.put(vehicleId, tripId);
// TODO - New rule - check that there is at most one TripUpdate entity per scheduled trip_id - see https://github.com/CUTR-at-USF/gtfs-realtime-validator/issues/33
}
}
if (entity.hasVehicle() && hasVehicleId(entity.getVehicle())) {
vehiclePositionCount++;
String vehicleId = entity.getVehicle().getVehicle().getId();
String tripId = "";
if (entity.getVehicle().hasTrip() && entity.getVehicle().getTrip().hasTripId()) {
tripId = entity.getVehicle().getTrip().getTripId();
}
if (StringUtils.isEmpty(tripId)) {
// Vehicle does not have a trip_id - add it to the set
vehiclesWithoutTrips.add(vehicleId);
} else {
// Vehicle has a trip_id - add it to the HashMap
vehiclePositionsVehicleIdToTripId.put(vehicleId, tripId);
vehiclePositionsTripIdToVehicleId.put(tripId, vehicleId);
// TODO - New rule - check that there is at most one vehicle assigned each trip - see https://github.com/CUTR-at-USF/gtfs-realtime-validator/issues/38
}
}
}
List<ErrorListHelperModel> errors = new ArrayList<>();
if (tripUpdateCount == 0 || vehiclePositionCount == 0) {
// We are missing a VehiclePositions or TripUpdates feed, so we can't compare across feeds - return empty list;
return errors;
}
// Check all trips that contained a vehicle
for (Map.Entry<String, String> trip : tripUpdatesTripIdToVehicleId.entrySet()) {
if (!vehiclePositionsTripIdToVehicleId.containsKey(trip.getKey())) {
// W003 - TripUpdates feed has a trip_id that's not in VehiclePositions feed
RuleUtils.addOccurrence(W003, "trip_id " + trip.getKey() + " is in TripUpdates but not in VehiclePositions feed", w003List, _log);
}
if (!vehiclePositionsVehicleIdToTripId.containsKey(trip.getValue()) && !vehiclesWithoutTrips.contains(trip.getValue())) {
// W003 - TripUpdates feed has a vehicle_id that's not in VehiclePositions feed
RuleUtils.addOccurrence(W003, "vehicle_id " + trip.getValue() + " is in TripUpdates but not in VehiclePositions feed", w003List, _log);
}
checkE047TripUpdates(trip, vehiclePositionsTripIdToVehicleId, e047List);
}
// Check all vehicles that contained a trip
for (Map.Entry<String, String> vehiclePosition : vehiclePositionsVehicleIdToTripId.entrySet()) {
if (!tripUpdatesVehicleIdToTripId.containsKey(vehiclePosition.getKey())) {
// W003 - VehiclePositions has a vehicle_id that's not in TripUpdates feed
RuleUtils.addOccurrence(W003, "vehicle_id " + vehiclePosition.getKey() + " is in VehiclePositions but not in TripUpdates feed", w003List, _log);
}
if (!tripUpdatesTripIdToVehicleId.containsKey(vehiclePosition.getValue()) && !tripsWithoutVehicles.contains(vehiclePosition.getValue())) {
// W003 - VehiclePositions has a trip_id that's not in the TripUpdates feed
RuleUtils.addOccurrence(W003, "trip_id " + vehiclePosition.getValue() + " is in VehiclePositions but not in TripUpdates feed", w003List, _log);
}
checkE047VehiclePositions(vehiclePosition, tripUpdatesVehicleIdToTripId, gtfsMetadata, e047List);
}
// Check all trips that did NOT contain a vehicle
for (String trip_id : tripsWithoutVehicles) {
if (!vehiclePositionsTripIdToVehicleId.containsKey(trip_id)) {
// W003 - TripUpdates feed has a trip_id that's not in VehiclePositions feed
RuleUtils.addOccurrence(W003, "trip_id " + trip_id + " is in TripUpdates but not in VehiclePositions feed", w003List, _log);
}
}
// Check all vehicles that did NOT contain a trip
for (String vehicle_id : vehiclesWithoutTrips) {
if (!tripUpdatesVehicleIdToTripId.containsKey(vehicle_id)) {
// W003 - VehiclePositions has a vehicle_id that's not in TripUpdates feed
RuleUtils.addOccurrence(W003, "vehicle_id " + vehicle_id + " is in VehiclePositions but not in TripUpdates feed", w003List, _log);
}
}
if (!w003List.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(W003), w003List));
}
if (!e047List.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E047), e047List));
}
return errors;
}
use of edu.usf.cutr.gtfsrtvalidator.lib.model.MessageLogModel in project gtfs-realtime-validator by CUTR-at-USF.
the class FrequencyTypeZeroValidator method validate.
@Override
public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsDaoImpl 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<>();
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
*/
if (!tripUpdate.getTrip().hasStartDate()) {
// E006 - Missing required trip_update trip field for frequency-based exact_times = 0
RuleUtils.addOccurrence(E006, "trip_id " + tripUpdate.getTrip().getTripId() + " is missing start_date", errorListE006, _log);
}
if (!tripUpdate.getTrip().hasStartTime()) {
// E006 - Missing required trip_update trip field for frequency-based exact_times = 0
RuleUtils.addOccurrence(E006, "trip_id " + tripUpdate.getTrip().getTripId() + " is missing start_time", errorListE006, _log);
}
if (!(!tripUpdate.getTrip().hasScheduleRelationship() || tripUpdate.getTrip().getScheduleRelationship().equals(GtfsRealtime.TripDescriptor.ScheduleRelationship.UNSCHEDULED))) {
// E013 - Validate schedule_relationship is UNSCHEDULED or empty
RuleUtils.addOccurrence(E013, "trip_id " + tripUpdate.getTrip().getTripId() + " schedule_relationship " + tripUpdate.getTrip().getScheduleRelationship(), errorListE013, _log);
}
if (!tripUpdate.hasVehicle() || !tripUpdate.getVehicle().hasId()) {
// W005 - Missing vehicle_id in trip_update for frequency-based exact_times = 0
RuleUtils.addOccurrence(W005, "trip_id " + tripUpdate.getTrip().getTripId(), errorListW005, _log);
}
}
}
if (entity.hasVehicle()) {
GtfsRealtime.VehiclePosition vehiclePosition = entity.getVehicle();
if (vehiclePosition.hasTrip() && gtfsMetadata.getExactTimesZeroTripIds().contains(vehiclePosition.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
*/
if (!vehiclePosition.getTrip().hasStartDate()) {
// E006 - Missing required vehicle_position trip field for frequency-based exact_times = 0
RuleUtils.addOccurrence(E006, "vehicle_id " + vehiclePosition.getVehicle().getId() + " trip_id " + vehiclePosition.getTrip().getTripId() + " is missing start_date", errorListE006, _log);
}
if (!vehiclePosition.getTrip().hasStartTime()) {
// E006 - Missing required vehicle_position trip field for frequency-based exact_times = 0
RuleUtils.addOccurrence(E006, "vehicle_id " + vehiclePosition.getVehicle().getId() + " trip_id " + vehiclePosition.getTrip().getTripId() + " is missing start_time", errorListE006, _log);
}
if (!(!vehiclePosition.getTrip().hasScheduleRelationship() || vehiclePosition.getTrip().getScheduleRelationship().equals(GtfsRealtime.TripDescriptor.ScheduleRelationship.UNSCHEDULED))) {
// E013 - Validate schedule_relationship is UNSCHEDULED or empty
String prefix = "vehicle_id " + vehiclePosition.getVehicle().getId() + " trip_id " + vehiclePosition.getTrip().getTripId() + " schedule_relationship " + vehiclePosition.getTrip().getScheduleRelationship();
RuleUtils.addOccurrence(E013, prefix, errorListE013, _log);
}
if (!vehiclePosition.getVehicle().hasId()) {
// W005 - Missing vehicle_id for frequency-based exact_times = 0
RuleUtils.addOccurrence(W005, "entity ID" + entity.getId() + "with trip_id " + vehiclePosition.getTrip().getTripId(), errorListW005, _log);
}
}
}
}
List<ErrorListHelperModel> errors = new ArrayList<>();
if (!errorListE006.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E006), errorListE006));
}
if (!errorListE013.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E013), errorListE013));
}
if (!errorListW005.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(W005), errorListW005));
}
return errors;
}
use of edu.usf.cutr.gtfsrtvalidator.lib.model.MessageLogModel in project gtfs-realtime-validator by CUTR-at-USF.
the class FrequencyTypeOneValidator method validate.
@Override
public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsDaoImpl gtfsData, GtfsMetadata gtfsMetadata, GtfsRealtime.FeedMessage feedMessage, GtfsRealtime.FeedMessage previousFeedMessage, GtfsRealtime.FeedMessage combinedFeedMessage) {
List<OccurrenceModel> errorListE019 = new ArrayList<>();
for (GtfsRealtime.FeedEntity entity : feedMessage.getEntityList()) {
if (entity.hasTripUpdate()) {
GtfsRealtime.TripUpdate tripUpdate = entity.getTripUpdate();
List<Frequency> frequenceTypeOneList = gtfsMetadata.getExactTimesOneTrips().get(tripUpdate.getTrip().getTripId());
if (frequenceTypeOneList != null) {
boolean foundMatch = false;
String gtfsStartTimeString = null;
Integer headwaySecs = null;
// For at least one frequency period for this trip_id, start_time in the GTFS-rt data must be some multiple (including zero) of headway_secs later than the start_time
for (Frequency f : frequenceTypeOneList) {
int startTime = f.getStartTime();
// See if the GTFS-rt start_time matches at least one multiple of GTFS start_time for this frequency
while (startTime < f.getEndTime()) {
// Convert seconds after midnight to 24hr clock time like "06:00:00"
gtfsStartTimeString = TimestampUtils.secondsAfterMidnightToClock(startTime);
headwaySecs = f.getHeadwaySecs();
_log.debug("start time = " + startTime);
_log.debug("formatted start time = " + gtfsStartTimeString);
if (tripUpdate.getTrip().getStartTime().equals(gtfsStartTimeString)) {
// We found a matching multiple - no error for this GTFS-rt start_time
foundMatch = true;
break;
}
startTime += f.getHeadwaySecs();
}
if (foundMatch) {
// If we found at least one matching frequency with a matching multiple of headway_secs for the GTFS-rt start_time, then no error
break;
}
}
if (!foundMatch) {
// E019 - GTFS-rt frequency exact_times = 1 trip start_time must match GTFS data
String prefix = "GTFS-rt trip_id " + tripUpdate.getTrip().getTripId() + " has start_time of " + tripUpdate.getTrip().getStartTime() + " and GTFS frequencies.txt start_time is " + gtfsStartTimeString + " with a headway of " + headwaySecs + " seconds ";
RuleUtils.addOccurrence(E019, prefix, errorListE019, _log);
}
}
}
if (entity.hasVehicle()) {
GtfsRealtime.VehiclePosition vehiclePosition = entity.getVehicle();
// E019 - GTFS-rt frequency exact_times = 1 trip start_date and start_time must match GTFS data
List<Frequency> frequenceTypeOneList = gtfsMetadata.getExactTimesOneTrips().get(vehiclePosition.getTrip().getTripId());
if (frequenceTypeOneList != null) {
boolean foundMatch = false;
String gtfsStartTimeString = null;
Integer headwaySecs = null;
// For at least one frequency period for this trip_id, start_time in the GTFS-rt data must be some multiple (including zero) of headway_secs later than the start_time
for (Frequency f : frequenceTypeOneList) {
int startTime = f.getStartTime();
// See if the GTFS-rt start_time matches at least one multiple of GTFS start_time for this frequency
while (startTime < f.getEndTime()) {
// Convert seconds after midnight to 24hr clock time like "06:00:00"
gtfsStartTimeString = String.format("%02d:%02d:%02d", startTime / 3600, startTime % 360, startTime % 60);
headwaySecs = f.getHeadwaySecs();
_log.debug("start time = " + startTime);
_log.debug("formatted start time = " + gtfsStartTimeString);
if (vehiclePosition.hasTrip() && vehiclePosition.getTrip().getStartTime().equals(gtfsStartTimeString)) {
// We found a matching multiple - no error for this GTFS-rt start_time
foundMatch = true;
break;
}
startTime += f.getHeadwaySecs();
}
if (foundMatch) {
// If we found at least one matching frequency with a matching multiple of headway_secs for the GTFS-rt start_time, then no error
break;
}
}
if (!foundMatch) {
// E019 - GTFS-rt frequency exact_times = 1 trip start_time must match GTFS data
String prefix = "GTFS-rt trip_id " + vehiclePosition.getTrip().getTripId() + " has start_time of " + vehiclePosition.getTrip().getStartTime() + " and GTFS frequencies.txt start_time is " + gtfsStartTimeString + " with a headway of " + headwaySecs + " seconds ";
RuleUtils.addOccurrence(E019, prefix, errorListE019, _log);
}
}
}
}
List<ErrorListHelperModel> errors = new ArrayList<>();
if (!errorListE019.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E019), errorListE019));
}
return errors;
}
use of edu.usf.cutr.gtfsrtvalidator.lib.model.MessageLogModel in project gtfs-realtime-validator by CUTR-at-USF.
the class HeaderValidator method validate.
@Override
public List<ErrorListHelperModel> validate(long currentTimeMillis, GtfsDaoImpl gtfsData, GtfsMetadata gtfsMetadata, GtfsRealtime.FeedMessage feedMessage, GtfsRealtime.FeedMessage previousFeedMessage, GtfsRealtime.FeedMessage combinedFeedMessage) {
List<OccurrenceModel> errorListE038 = new ArrayList<>();
List<OccurrenceModel> errorListE039 = new ArrayList<>();
List<OccurrenceModel> errorListE049 = new ArrayList<>();
if (!GtfsUtils.isValidVersion(feedMessage.getHeader())) {
// E038 - Invalid header.gtfs_realtime_version
RuleUtils.addOccurrence(E038, "header.gtfs_realtime_version of " + feedMessage.getHeader().getGtfsRealtimeVersion(), errorListE038, _log);
}
try {
if (GtfsUtils.isV2orHigher(feedMessage.getHeader()) && !feedMessage.getHeader().hasIncrementality()) {
// E049 - header incrementality not populated
RuleUtils.addOccurrence(E049, "", errorListE049, _log);
}
} catch (Exception e) {
_log.error("Error checking header version for E049: " + e);
}
if (feedMessage.getHeader().getIncrementality().equals(GtfsRealtime.FeedHeader.Incrementality.FULL_DATASET)) {
for (GtfsRealtime.FeedEntity entity : feedMessage.getEntityList()) {
if (entity.hasIsDeleted()) {
// E039 - FULL_DATASET feeds should not include entity.is_deleted
RuleUtils.addOccurrence(E039, "entity ID " + entity.getId() + " has is_deleted=" + entity.getIsDeleted(), errorListE039, _log);
}
}
}
List<ErrorListHelperModel> errors = new ArrayList<>();
if (!errorListE038.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E038), errorListE038));
}
if (!errorListE039.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E039), errorListE039));
}
if (!errorListE049.isEmpty()) {
errors.add(new ErrorListHelperModel(new MessageLogModel(E049), errorListE049));
}
return errors;
}
Aggregations