trips_updated
event type: com.mbta.ctd.glides.trips_updated.v1
Published when Glides has new information about trips, for example operator assignments or dropped trips. Consumers who want to know what trains are running should pay attention to this event, and don't need to pay attention to any other events.
Data
Fields in the event's data
:
metadata
(Metadata): how the updates were entered in Glides, including the inspector who made the change and the location of the trainsheet.tripUpdates
(array of (TripUpdated|TripAdded)): The list of one or more trips that have new information.
Car
Fields:
label
(string |"none"
, optional): car number corresponding to the GTFS-RTlabel
field. Or"none"
if the inspector has unassigned the car. If the field is absent, it is not modified.operator
(Operator |"none"
|"unset"
, optional): the operator assigned to this car. The string"none"
means the inspector has unassigned the operator without reassigning another. The string"unset"
means to discard the previous value as if the field had never been modified. If the field is absent, it is not modified from its previous value.
"unset"
and "none"
are semantically different, and used consistently with the "unset"
values in TripUpdated. If an operator is "unset"
, then, as if no update had ever been made, clients SHOULD assume the scheduled operator will operate the car. If an operator is "none"
, then we don't know who will operate the car.
An empty object {}
is valid if nothing about the car has been modified.
The length of cars
always reflects the known length of the train.
DroppedReason
An object representing the reason that a trip was dropped. The presence of this object implies that the trip was dropped.
Fields:
reason
(string): free-text description about why a trip was dropped.
Currently the reason is entered by inspectors in a free-text field. It's published in an object so that if Glides collects more structured data in the future, then extra fields can be added. If more fields are added, then a generated text description would be filled into the reason
field, and it would not be a breaking change.
reason
MAY be the empty string if for some reason Glides does not have information about why a trip was dropped.
Location
See Location
Metadata
See Metadata
Operator
See Operator
Scheduled
Scheduled information about the trip. It was not updated in Glides, but is included in the event stream so that consumers can know the schedule information that Glides uses. If data here was never overriden by a corresponding field in TripUpdated, then consumers can assume that the trip operated based on the scheduled information contained here.
Fields:
scheduledCars
(array of ScheduledCar): Array of length 1 or 2. The length reflects the scheduled length of the train. In a 2 car train, the front car is listed first.
It does not include time and location fields, because for scheduled trips those fields are already in TripKey
ScheduledCar
Fields:
run
(string, optional): The run number scheduled to the trip.operator
(Operator, optional): The operator who is scheduled to operate the run that day, and therefore scheduled to do the trip.
If fields are missing from ScheduledCar, it is not known who is scheduled to operate that car. An empty object is valid if we know the car is scheduled to operate but don't know anything else about it.
Time
Time in the HH:MM:SS
format. The time is measured from "noon minus 12h" of the service day (effectively midnight except for days on which daylight savings time changes occur). For times occurring after midnight, enter the time as a value greater than 24:00:00 in HH:MM:SS
local time for the day on which the trip schedule begins. Effectively, a GTFS Time but without support for H:MM:SS
times.
Example:
04:30:00
for 4:30AM or25:35:00
for 1:35AM on the next day.
TripAdded
There is a new trip, that does not appear in the Glides' schedule data. "Added" is relative to the schedule that Glides uses, and an added trip may correspond to a trip that appears in a different schedule.
Any added trips will appear exactly once as a TripAdded object, and any future updates will appear as TripUpdated objects. (But note: Clients MUST tolerate updates for trips they haven't seen due to network errors or the Kinesis trim horizon, see: Short-term storage. And clients MUST tolerate duplicates due to a network error, see: Idempotence.)
The event SHOULD only include values which were explicitly included by the author: inferred values SHOULD NOT be included.
TripAdded objects are the same as TripUpdated with the following new restrictions, so that enough is specified about the trip to know or infer where and when the trip will happen. (This extension means all TripAdded objects also happen to be valid TripUpdated objects according to the spec below.)
New fields:
previousTripKey
(TripKey, conditionally required): The added trip will happen immediately after the trip referred to bypreviousTripKey
. Required ifstartTime
andendTime
are both not set. (If no time is specified, then the previous trip is necessary so the trip's time can be inferred.) This field MAY refer to a trip that has not been already seen in the event stream.
New restrictions on existing fields:
type
(string): MUST be"added"
(as opposed to"updated"
).tripKey
(TripKey): will always be the added trip form, and never the scheduled trip form.startLocation
(Location, conditionally required): where the trip will start. Required ifstartTime
is specified.endLocation
(Location, conditionally required): where the trip will end. Required ifendTime
is specified.- At least one of
startLocation
andendLocation
is required. - At least one of
startTime
,endTime
, andpreviousTripKey
is required. revenue
("revenue"
|"nonrevenue"
, optional): If this field is missing when adding a trip, the default is that the trip is in revenue service.dropped
SHOULD NOT be set.- All data SHOULD NOT be
"none"
or"unset"
. (Instead, the fields would not be included).
Glides SHOULD include the cars
field to indicate the length of the train, even if no information is known about each car.
TripKey
The event has different ways to label trips that are added or scheduled in Glides' schedule.
The added/scheduled distinction is according to the schedule in Glides. RTR and GTFS may have a different schedule than Glides (due to disruptions, for example), so a trip that Glides thinks is scheduled may not match up with scheduled trip in GTFS, and RTR may decide that a trip that was added in Glides lines up close enough to correspond to a scheduled trip in GTFS.
For added trips, Glides makes up a new ID, which is kept the same for any subsequent updates to the same added trip.
For scheduled trips, because Glides might be using a different schedule than any consumer, Glides provides the time and location of the trip, according to its own schedule. Glides also provides a tripId
which is intended to line up to the trip IDs in GTFS most of the time, because they're derived from the same source (HASTUS's trp_int_number
).
Consumers MAY check tripId
to help match trips in this feed to GTFS. If a consumer sees a tripId
in this feed that also appears in GTFS on a trip on the same service date, it refers to the same trip (even if the given scheduled times aren't quite the same). If a tripId
for a trip in this feed does not appear on any trip in GTFS (or if the tripId
field is missing, or if a consumer doesn't check the tripId
field), then the consumer may use the date, time, and location of the trip to try to associate it to a similar trip in whatever schedule it's using, or a consumer may treat the trip as if does not correspond to any trip in the consumer's schedule.
tripId
is optional because it was added as a backwards-compatible change. Glides SHOULD include it in all future data.
Added Trip
serviceDate
(RFC3999 date): the service date for the trip.glidesId
(string): unique value representing this trip for the givenserviceDate
.
Example:
{ "serviceDate": "2023-01-20", "glidesId": "ADDED-123" }
Scheduled Trip
serviceDate
(RFC3999 date): the service date for the trip.tripId
(string, optional): ID, unique within the service date. Intended to match GTFS trip IDs, but may not.startLocation
(Location): where the trip is scheduled to start.endLocation
(Location): where the trip is scheduled to end.startTime
(Time): when the trip is scheduled to departstartLocation
.endTime
(Time): when the trip is scheduled to arrive atendLocation
.revenue
("revenue"
|"nonrevenue"
, optional): Whether the trip is scheduled to be in revenue service. If this field is missing, consumers SHOULD assume the trip is scheduled to be in revenue service.
Example:
{
"serviceDate": "2023-01-20",
"tripId": "64100439",
"startLocation": { "gtfsId": "place-mdftf" },
"endLocation": { "gtfsId": "place-heath" },
"startTime": "04:47:00",
"endTime": "05:34:00"
}
TripUpdated
This indicates that a trip was updated in some fashion: e.g. consist, operators, departure time. The trip is either a scheduled trip, or an added trip that has already appeared in the events stream. A field that is not present was not updated.
Fields in the object:
type
(string):"updated"|"added"
. If this field is"updated"
, the object is this TripUpdated object. If it's"added"
, it's a TripAdded object that extends TripUpdated and has all these fields but with additional meaning, fields, and restrictions, see above. Subsequent updates to previously-added trips havetype
"updated"
.tripKey
(TripKey): which trip is being updated.comment
(string, optional): free text information about the trip. Could potentially be the empty string, if a comment was deleted.startLocation
(Location | "unset", optional): the new destination of the train.endLocation
(Location | "unset", optional): the new destination of the train.startTime
(Time | "unset", optional): if present, the new time that the train is expected to departstartLocation
(or the existingstartLocation
of the trip).endTime
(Time | "unset", optional): if present, the new time that the train is expected to arrive atendLocation
(or the existingendLocation
of the trip).cars
(array of Car, optional): array of length 1 or 2, containing the car numbers and operators for each car in the train assigned to the trip. If absent, there are no changes to any cars. If present, the length of the array is the length of the train. In a two car train, the front car is listed first.revenue
("revenue"
|"nonrevenue"
, optional): Whether the trip is in revenue service.dropped
(DroppedReason | false, optional): If present the trip was dropped for the provided reason. If set tofalse
, the trip is not dropped (and restored if previously dropped).scheduled
(Scheduled | null): For trips in Glides' schedule, this represents the scheduled data for the trip. It will always be present and won't change between subsequent updates to the same trip. For added trips, this will always benull
.
Some values might be the special string "unset"
. This means to treat the field as if it had never been modified. This is semantically slightly different than setting the value to the scheduled value. For example, setting startTime
to "unset"
implies we don't know when the trip will leave, and the scheduled time might be a good guess, but setting startTime
to equal the scheduled start time implies that an inspector has confirmed the trip will leave at that time.
Notes:
- setting a new location or time does not modify the TripKey for a scheduled trip.
- If
cars
is updated from a 1-element list to a 2-element list, then Glides SHOULD include data for the 2nd car. This is relevant if a two-car train has a car removed and then re-added. If a field was set before the car was dropped, then when the car is restored, clients MUST assume that the field is"none"
as opposed to retaining its previous value, but Glides SHOULD include the data to avoid the ambiguity. - Some fields, such as
startTime
orcars
, are not relevant to dropped trips. Those fields SHOULD NOT be set in an update that drops a trip or any following updates, until the trip is undropped by settingdropped
tofalse
. However, if a trip is undropped, clients MUST assume all fields retain their values from before the field was undropped. Clients MUST NOT ignore updates to fields when a trip is dropped, even if they aren't relevant for dropped trips. For example, if a trip is dropped, and then thestartTime
is updated, and then it's undropped, the trip'sstartTime
is the value set while the trip was dropped. Glides MAY include extra fields in an update that undrops a trip, if they are changed as part of the same event that undropped the trip, or just to remind clients about them now that they're relevant.
Examples
- 15 Minute Delay
- Dropped Trip and Manage Headways
- Splitting a Train (Added Trip)
Inspector Alice (badge number: 123) is working at Mattapan. The 1:30am trip is being held 15 minutes to wait for a final Red Line train.
Note that the date of the the event is 2023-01-23, but the service date is 2023-01-22. The hour in the time itself is greater than 24 hours, to reflect that the time is the next calendar day.
{
"type": "com.mbta.ctd.glides.trips_updated.v1",
"specversion": "1.0",
"source": "glides.mbta.com",
"id": "19fdb184-7dd6-4664-8472-04bd6177ec44",
"time": "2023-01-23T01:25:00-05:00",
"data": {
"metadata": {
"inputType": "edit-trip"
},
"tripUpdates": [
{
"type": "updated",
"tripKey": {
"serviceDate": "2023-01-22",
"tripId": "64085858",
"startLocation": {
"gtfsId": "place-matt"
},
"endLocation": {
"gtfsId": "place-ashmt"
},
"startTime": "25:30:00",
"endTime": "25:38:00"
},
"startTime": "25:45:00",
"scheduled": {
"scheduledCars": [
{
"run": "500",
"operator": {
"badgeNumber": "1234"
}
}
]
}
}
]
}
}
There are four scheduled trips going to Government Center: 9:55am, 10:00am, 10:05am, and 10:10am.
Due to staffing issues, Inspector Alice drops the 10:05am trip, along with its return trip. (First event with two updated trips).
Using the "Manage Headways" feature, Alice changes the expected departure times of the remaining trips to 9:56am, 10:02am, and 10:08am. (Second event with three updated trips).
[
{
"type": "com.mbta.ctd.glides.trips_updated.v1",
"specversion": "1.0",
"source": "glides.mbta.com",
"id": "19fdb184-7dd6-4664-8472-04bd6177ec44",
"time": "2023-01-20T09:30:00-05:00",
"data": {
"metadata": {
"inputType": "dropped-trip"
},
"tripUpdates": [
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101112",
"startLocation": {
"gtfsId": "place-lake"
},
"endLocation": {
"gtfsId": "place-gover"
},
"startTime": "10:05:00",
"endTime": "10:52:00"
},
"dropped": {
"reason": "staffing"
},
"scheduled": {
"scheduledCars": [
{}
]
}
},
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101110",
"startLocation": {
"gtfsId": "place-gover"
},
"endLocation": {
"gtfsId": "place-lake"
},
"startTime": "10:05:00",
"endTime": "10:42:00"
},
"dropped": {
"reason": "staffing"
},
"scheduled": {
"scheduledCars": [
{}
]
}
}
]
}
},
{
"type": "com.mbta.ctd.glides.trips_updated.v1",
"specversion": "1.0",
"source": "glides.mbta.com",
"id": "19fdb184-7dd6-4664-8472-04bd6177ec44",
"time": "2023-01-20T09:30:00-05:00",
"data": {
"metadata": {
"inputType": "manage-headways"
},
"tripUpdates": [
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101093",
"startLocation": {
"gtfsId": "place-lake"
},
"endLocation": {
"gtfsId": "place-gover"
},
"startTime": "09:55:00",
"endTime": "10:42:00"
},
"startTime": "09:56:00",
"scheduled": {
"scheduledCars": [
{}
]
}
},
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101094",
"startLocation": {
"gtfsId": "place-lake"
},
"endLocation": {
"gtfsId": "place-gover"
},
"startTime": "10:00:00",
"endTime": "10:47:00"
},
"startTime": "10:02:00",
"scheduled": {
"scheduledCars": [
{}
]
}
},
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101095",
"startLocation": {
"gtfsId": "place-lake"
},
"endLocation": {
"gtfsId": "place-gover"
},
"startTime": "10:10:00",
"endTime": "10:57:00"
},
"startTime": "10:08:00",
"scheduled": {
"scheduledCars": [
{}
]
}
}
]
}
}
]
Splitting a 2-car train into two 1-car trips.
There are two scheduled trips: 9:55am and 10:00am. Operator A (badge number: 456) and Operator B (badge number: 567) are scheduled to perform the 9:55am. But only 1 trainset (cars 3800 and 3850) is available. In order to maintain headways, Inspector Alice splits the trainset. Operator A will take car 3800 and run the 9:55am. Operator B will take car 3850 and run the 10:00am.
Alice does this in 3 actions:
- Drop the 10:00am trip.
- Edit the 9:55am trip to have only one car, with Operator A and car 3800. Note the length of `"cars"` is 1 but the length of `"scheduledCars"` is still 2.
- Add a new trip, set to 10:00am with Operator B and car 3850. Alice added it as a round trip, so there's also an added return trip with no set time.
(In real life, Alice would probably assign B to the 2nd trip instead of dropping it and adding a new trip. The example does it this way to provide an example of adding a trip. Ideally, consumers would treat both ways the same, because they describe the same service.)
[
{
"type": "com.mbta.ctd.glides.trips_updated.v1",
"specversion": "1.0",
"source": "glides.mbta.com",
"id": "19fdb184-7dd6-4664-8472-04bd6177ec44",
"time": "2023-01-20T09:30:00-05:00",
"data": {
"metadata": {
"inputType": "dropped-trip"
},
"tripUpdates": [
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101244",
"startLocation": {
"gtfsId": "place-lake"
},
"endLocation": {
"gtfsId": "place-gover"
},
"startTime": "10:00:00",
"endTime": "10:47:00"
},
"dropped": {
"reason": "ran as single"
},
"scheduled": {
"scheduledCars": [
{
"run": "506",
"operator": {
"badgeNumber": "678"
}
},
{
"run": "507",
"operator": {
"badgeNumber": "789"
}
}
]
}
}
]
}
},
{
"type": "com.mbta.ctd.glides.trips_updated.v1",
"specversion": "1.0",
"source": "glides.mbta.com",
"id": "19fdb184-7dd6-4664-8472-04bd6177ec44",
"time": "2023-01-20T09:30:00-05:00",
"data": {
"metadata": {
"inputType": "edit-trip"
},
"tripUpdates": [
{
"type": "updated",
"tripKey": {
"serviceDate": "2022-01-20",
"tripId": "64101243",
"startLocation": {
"gtfsId": "place-lake"
},
"endLocation": {
"gtfsId": "place-gover"
},
"startTime": "09:55:00",
"endTime": "10:42:00"
},
"comment": "single",
"cars": [
{
"label": "3800",
"operator": {
"badgeNumber": "456"
}
}
],
"scheduled": {
"scheduledCars": [
{
"run": "504",
"operator": {
"badgeNumber": "456"
}
},
{
"run": "505",
"operator": {
"badgeNumber": "567"
}
}
]
}
}
]
}
},
{
"type": "com.mbta.ctd.glides.trips_updated.v1",
"specversion": "1.0",
"source": "glides.mbta.com",
"id": "19fdb184-7dd6-4664-8472-04bd6177ec44",
"time": "2023-01-20T09:30:00-05:00",
"data": {
"metadata": {
"inputType": "add-trip"
},
"tripUpdates": [
{
"type": "added",
"tripKey": {
"serviceDate": "2022-01-20",
"glidesId": "ADDED-1"
},
"startLocation": {
"gtfsId": "place-lake"
},
"startTime": "10:00:00",
"cars": [
{
"label": "3850",
"operator": {
"badgeNumber": "567"
}
}
],
"scheduled": null
},
{
"type": "added",
"tripKey": {
"serviceDate": "2022-01-20",
"glidesId": "ADDED-2"
},
"endLocation": {
"gtfsId": "place-lake"
},
"previousTripKey": {
"serviceDate": "2022-01-20",
"glidesId": "ADDED-1"
},
"cars": [
{
"label": "3850",
"operator": {
"badgeNumber": "567"
}
}
],
"scheduled": null
}
]
}
}
]
Schema
Unsupported Features
Mid-route data
This event has no way to publish data about the middle of trips, only the endpoints. This is because Glides does not currently collect data from inspectors about the middles of trips. It's likely that in the future, Glides might add features to allow Chief Inspectors, Dispatchers, or others to input short turns, holds, expresses, or other adjustments, in which case this event would be updated or another event would be added.
More specific events
This event lumps all service updates into a single event. Other proposals considered having separate events for trip_added
, trip_dropped
, or even separate events for updating each field. This would have portrayed inspectors' intent more clearly, and allowed more specific data to be sent in each case.
However, it is easier for consumers to understand the effects on service from this single state-transfer event.
Consumers who want more transparency into how an official entered the update in Glides should look at the metadata.inputType
field.
For more explanation, see the alternatives section of RFC 18.
Include all known data, not just changes
This event only includes updated fields, which means that clients must listen and remember all past changes if they want to know the state of a trip. In the future, if a client needs it, Glides could start including in this event a snapshot of all known data about the trip, but there hasn't been a concrete reason to add it yet. For more explanation, see the alternatives section of RFC 18. See also: Kinds of Events