4.4 KiB · text History 011a33a

GTFS-realtime protobuf definitions

These generate the TransitRealtime.* modules in lib/. They are vendored rather than fetched at build time so a build does not depend on four upstream URLs staying up, and so a change to what we parse shows up as a diff.

File Upstream Generates
gtfs-realtime.proto google/transit lib/gtfs_realtime_pb.ex
gtfs-realtime-NYCT.proto OneBusAway lib/gtfs_realtime_nyct_pb.ex
gtfs-realtime-MTARR.proto OneBusAway lib/gtfs_realtime_mtarr_pb.ex
gtfs-realtime-service-status.proto OneBusAway lib/gtfs_realtime_service_status_pb.ex

All four extend declarations across the three MTA files land in one lib/gtfs_realtime_pb_extension.ex — that is how protobuf-elixir works, one extension module per generated set.

Regenerating

There is no protoc in this repo and no need for one to be installed globally. Fetch a release binary, and use the plugin that ships in deps/protobuf by running it off the umbrella’s own compiled beams:

cd /tmp && curl -sSLO https://github.com/protocolbuffers/protobuf/releases/download/v29.3/protoc-29.3-linux-x86_64.zip
unzip -q protoc-29.3-linux-x86_64.zip -d protoc

cat > /tmp/protoc-gen-elixir <<'SH'
#!/usr/bin/env bash
exec elixir \
  -pa <umbrella>/_build/dev/lib/protobuf/ebin \
  -pa <umbrella>/_build/dev/lib/jason/ebin \
  -e 'Application.load(:protobuf); Protobuf.Protoc.CLI.main([])'
SH
chmod +x /tmp/protoc-gen-elixir

Application.load(:protobuf) is load-bearing: the generator stamps its own version into every file and reads it from the application spec, so without it generation dies with MatchError: :undefined.

Then, from apps/room_gtfs:

/tmp/protoc/bin/protoc \
  --elixir_out=/tmp/out \
  --plugin=protoc-gen-elixir=/tmp/protoc-gen-elixir \
  -I priv/proto \
  priv/proto/com/google/transit/realtime/gtfs-realtime.proto \
  priv/proto/com/google/transit/realtime/gtfs-realtime-NYCT.proto \
  priv/proto/com/google/transit/realtime/gtfs-realtime-MTARR.proto \
  priv/proto/com/google/transit/realtime/gtfs-realtime-service-status.proto

and copy the results over the five files in lib/ named in the table above. The -I priv/proto matters: the MTA files import "com/google/transit/realtime/gtfs-realtime.proto", so the directory layout under priv/proto has to match that path.

The one local edit

gtfs-realtime-NYCT.proto and gtfs-realtime-service-status.proto both claim extension number 1001 on FeedHeader — nyct_feed_header and mercury_feed_header. protobuf-elixir keys its extension registry on {extendee, tag}, so declaring both collapses them into one entry and whichever is generated later wins. A subway trip feed’s header would then decode into Mercury’s struct, and get_extension/3 would hand it back under either name without complaint. protoc warns about this; it is not an error.

Both extend blocks are therefore commented out in place, with the reasoning at the edit. Nothing here reads the feed header beyond timestamp, which is a plain spec field, so this costs nothing today. Restoring either means first deciding which of the two owns FeedHeader#1001.

Everything else the MTA extends is on a distinct extendee — TripDescriptor, StopTimeUpdate, Alert, EntitySelector, VehiclePosition.CarriageDetails — and is unaffected.

Not vendored

MTA Bus vehicle positions carry an extension at VehiclePosition#1006 that none of these files define, and which is not OneBusAway’s either (theirs is 1000). On the wire it is two varints, the first 1–53 and the second always 65, 80 or 100, the first never exceeding the second — passenger count and vehicle capacity, almost certainly automatic passenger counting. Deliberately left unparsed until there is a definition to name the fields from; it shows up in __unknown_fields__ and is ignored.