diff --git a/lib/content/audio/following_train.ex b/lib/content/audio/following_train.ex index 458daa4c..0c05db7d 100644 --- a/lib/content/audio/following_train.ex +++ b/lib/content/audio/following_train.ex @@ -4,7 +4,7 @@ defmodule Content.Audio.FollowingTrain do """ @enforce_keys [:destination, :route_id, :verb, :minutes] - defstruct @enforce_keys ++ [station_code: nil] + defstruct @enforce_keys @type verb :: :arrives | :departs @@ -12,8 +12,7 @@ defmodule Content.Audio.FollowingTrain do destination: PaEss.destination(), route_id: String.t(), verb: verb(), - minutes: integer(), - station_code: String.t() | nil + minutes: integer() } require Logger @@ -25,7 +24,6 @@ defmodule Content.Audio.FollowingTrain do minutes: n, destination: destination, prediction: prediction, - station_code: station_code, terminal?: terminal }) when is_integer(n) do @@ -34,8 +32,7 @@ defmodule Content.Audio.FollowingTrain do destination: destination, route_id: prediction.route_id, minutes: n, - verb: arrives_or_departs(terminal), - station_code: station_code + verb: arrives_or_departs(terminal) } ] end diff --git a/lib/content/audio/next_train_countdown.ex b/lib/content/audio/next_train_countdown.ex index 56fc0819..1f11706b 100644 --- a/lib/content/audio/next_train_countdown.ex +++ b/lib/content/audio/next_train_countdown.ex @@ -4,7 +4,7 @@ defmodule Content.Audio.NextTrainCountdown do """ @enforce_keys [:destination, :route_id, :verb, :minutes, :track_number] - defstruct @enforce_keys ++ [:station_code, :zone, platform: nil] + defstruct @enforce_keys ++ [:special_sign, platform: nil] @type verb :: :arrives | :departs @@ -15,8 +15,7 @@ defmodule Content.Audio.NextTrainCountdown do minutes: integer(), track_number: Content.Utilities.track_number() | nil, platform: Content.platform() | nil, - station_code: String.t() | nil, - zone: String.t() | nil + special_sign: :jfk_mezzanine | :bowdoin_eastbound | nil } require Logger @@ -31,8 +30,7 @@ defmodule Content.Audio.NextTrainCountdown do verb: if(message.terminal?, do: :departs, else: :arrives), track_number: Content.Utilities.stop_track_number(message.prediction.stop_id), platform: Content.Utilities.stop_platform(message.prediction.stop_id), - station_code: message.station_code, - zone: message.zone + special_sign: message.special_sign } ] end @@ -74,7 +72,7 @@ defmodule Content.Audio.NextTrainCountdown do audio.minutes == 1 -> {:canned, {"142", [dest_var, platform_var(audio), verb_var(audio)], :audio}} - audio.destination == :alewife and audio.station_code == "RJFK" and audio.zone == "m" and + audio.destination == :alewife and audio.special_sign == :jfk_mezzanine and audio.minutes > 5 -> platform_tbd_params( audio, diff --git a/lib/content/audio/train_is_boarding.ex b/lib/content/audio/train_is_boarding.ex index ee751512..105dd069 100644 --- a/lib/content/audio/train_is_boarding.ex +++ b/lib/content/audio/train_is_boarding.ex @@ -35,7 +35,7 @@ defmodule Content.Audio.TrainIsBoarding do track_number: Content.Utilities.stop_track_number(message.prediction.stop_id) } ] ++ - if message.station_code == "BBOW" && message.zone == "e" do + if message.special_sign == :bowdoin_eastbound do [%Audio.BoardingButton{}] else [] diff --git a/lib/content/message/predictions.ex b/lib/content/message/predictions.ex index 45838875..23c6e6ec 100644 --- a/lib/content/message/predictions.ex +++ b/lib/content/message/predictions.ex @@ -10,71 +10,42 @@ defmodule Content.Message.Predictions do yourself. """ - require Logger require Content.Utilities - @terminal_brd_seconds 30 - @terminal_prediction_offset_seconds -60 - @reverse_prediction_certainty 360 - - @enforce_keys [:destination, :minutes] - defstruct [ - :destination, - :minutes, - :approximate?, - :prediction, - :station_code, - :zone, - terminal?: false - ] + @enforce_keys [:destination, :minutes, :approximate?, :prediction, :special_sign, :terminal?] + defstruct @enforce_keys @type t :: %__MODULE__{ destination: PaEss.destination(), minutes: integer() | :boarding | :arriving | :approaching, approximate?: boolean(), prediction: Predictions.Prediction.t(), - station_code: String.t() | nil, - zone: String.t() | nil, + special_sign: :jfk_mezzanine | :bowdoin_eastbound | nil, terminal?: boolean() } - @spec non_terminal(Predictions.Prediction.t(), String.t(), String.t()) :: t() - def non_terminal(prediction, station_code, zone) do - # e.g., North Station which is non-terminal but has trips that begin there - predicted_time = prediction.seconds_until_arrival || prediction.seconds_until_departure - - certainty = - if prediction.seconds_until_arrival, - do: prediction.arrival_certainty, - else: prediction.departure_certainty - - {minutes, approximate?} = - cond do - prediction.stops_away == 0 -> {:boarding, false} - predicted_time <= 30 -> {:arriving, false} - predicted_time <= 60 -> {:approaching, false} - true -> compute_minutes(predicted_time, certainty) + @spec new(Predictions.Prediction.t(), boolean(), :jfk_mezzanine | :bowdoin_eastbound | nil) :: + t() + def new(%Predictions.Prediction{} = prediction, terminal?, special_sign) do + sec = + if terminal? do + prediction.seconds_until_departure - 60 + else + prediction.seconds_until_arrival || prediction.seconds_until_departure end - %__MODULE__{ - destination: Content.Utilities.destination_for_prediction(prediction), - minutes: minutes, - approximate?: approximate?, - prediction: prediction, - station_code: station_code, - zone: zone - } - end - - @spec terminal(Predictions.Prediction.t(), String.t(), String.t()) :: t() - def terminal(prediction, station_code, zone) do + min = round(sec / 60) stopped_at? = prediction.stops_away == 0 + reverse_prediction? = Signs.Utilities.Predictions.reverse_prediction?(prediction, terminal?) {minutes, approximate?} = - case prediction.seconds_until_departure + @terminal_prediction_offset_seconds do - x when x <= @terminal_brd_seconds and stopped_at? -> {:boarding, false} - x when x <= @terminal_brd_seconds -> {1, false} - x -> compute_minutes(x, prediction.departure_certainty) + cond do + stopped_at? and (!terminal? or sec <= 30) -> {:boarding, false} + !terminal? and sec <= 30 -> {:arriving, false} + !terminal? and sec <= 60 -> {:approaching, false} + min > 60 -> {60, true} + reverse_prediction? and min > 20 -> {div(min, 10) * 10, true} + true -> {max(min, 1), false} end %__MODULE__{ @@ -82,43 +53,27 @@ defmodule Content.Message.Predictions do minutes: minutes, approximate?: approximate?, prediction: prediction, - station_code: station_code, - zone: zone, - terminal?: true + special_sign: special_sign, + terminal?: terminal? } end - defp compute_minutes(sec, certainty) do - min = round(sec / 60) - - cond do - min > 60 -> {60, true} - certainty == @reverse_prediction_certainty && min > 20 -> {div(min, 10) * 10, true} - true -> {min, false} - end - end - defimpl Content.Message do - require Logger - @width 18 - @boarding "BRD" - @arriving "ARR" - def to_string(%{ + def to_string(%Content.Message.Predictions{ destination: destination, minutes: minutes, approximate?: approximate?, prediction: %{stop_id: stop_id}, - station_code: station_code, - zone: zone + special_sign: special_sign }) do headsign = PaEss.Utilities.destination_to_sign_string(destination) duration_string = case minutes do - :boarding -> @boarding - :arriving -> @arriving + :boarding -> "BRD" + :arriving -> "ARR" :approaching -> "1 min" n -> "#{n}#{if approximate?, do: "+", else: ""} min" end @@ -126,7 +81,7 @@ defmodule Content.Message.Predictions do track_number = Content.Utilities.stop_track_number(stop_id) cond do - station_code == "RJFK" and destination == :alewife and zone == "m" -> + special_sign == :jfk_mezzanine and destination == :alewife -> platform_name = Content.Utilities.stop_platform_name(stop_id) {headsign_message, platform_message} = @@ -137,11 +92,7 @@ defmodule Content.Message.Predictions do end [ - {Content.Utilities.width_padded_string( - headsign_message, - "#{duration_string}", - @width - ), 6}, + {Content.Utilities.width_padded_string(headsign_message, duration_string, @width), 6}, {headsign <> platform_message, 6} ] @@ -155,10 +106,5 @@ defmodule Content.Message.Predictions do Content.Utilities.width_padded_string(headsign, duration_string, @width) end end - - def to_string(e) do - Logger.error("cannot_to_string: #{inspect(e)}") - "" - end end end diff --git a/lib/signs/utilities/audio.ex b/lib/signs/utilities/audio.ex index c27312d5..e93f42dc 100644 --- a/lib/signs/utilities/audio.ex +++ b/lib/signs/utilities/audio.ex @@ -321,9 +321,8 @@ defmodule Signs.Utilities.Audio do bottom.__struct__ in [Message.Predictions, Message.StoppedTrain] -> [{:predictions, [top, bottom]}] - # This only occurs at the RJFK mezzanine zone {%Message.Predictions{}, %Message.PlatformPredictionBottom{}} -> - [{:predictions, [%{top | zone: "m"}]}] + [{:predictions, [%{top | special_sign: :jfk_mezzanine}]}] {top, _} when top.__struct__ in [Message.Predictions, Message.StoppedTrain] -> [{:predictions, [top]}] diff --git a/lib/signs/utilities/messages.ex b/lib/signs/utilities/messages.ex index f59e1dc0..c04d7b11 100644 --- a/lib/signs/utilities/messages.ex +++ b/lib/signs/utilities/messages.ex @@ -5,7 +5,6 @@ defmodule Signs.Utilities.Messages do """ alias Content.Message.Alert - @reverse_prediction_certainty 360 @early_am_start ~T[03:29:00] @early_am_buffer -40 @@ -146,13 +145,12 @@ defmodule Signs.Utilities.Messages do defp expand_message( %Content.Message.Predictions{ - station_code: "RJFK", - zone: "m", + special_sign: :jfk_mezzanine, prediction: %{stop_id: stop_id}, minutes: minutes } = prediction ) do - {%{prediction | zone: nil}, + {%{prediction | special_sign: nil}, %Content.Message.PlatformPredictionBottom{stop_id: stop_id, minutes: minutes}} end @@ -206,8 +204,7 @@ defmodule Signs.Utilities.Messages do # except for Prudential or Symphony EB Enum.reject( predictions, - &(Signs.Utilities.Predictions.prediction_certainty(&1, config) == - @reverse_prediction_certainty and + &(Signs.Utilities.Predictions.reverse_prediction?(&1, config.terminal?) and &1.stop_id not in ["70240", "70242"]) ) end diff --git a/lib/signs/utilities/predictions.ex b/lib/signs/utilities/predictions.ex index 7849b096..1a4ac396 100644 --- a/lib/signs/utilities/predictions.ex +++ b/lib/signs/utilities/predictions.ex @@ -25,11 +25,7 @@ defmodule Signs.Utilities.Predictions do SourceConfig.config(), Signs.Realtime.t() ) :: Signs.Realtime.sign_messages() | nil - def prediction_messages( - predictions, - %{terminal?: terminal?}, - %{pa_ess_loc: station_code, text_zone: zone} = sign - ) do + def prediction_messages(predictions, %{terminal?: terminal?}, sign) do predictions |> Enum.filter(fn p -> p.seconds_until_departure && p.schedule_relationship != :skipped @@ -46,18 +42,19 @@ defmodule Signs.Utilities.Predictions do end) |> filter_large_red_line_gaps() |> Enum.map(fn prediction -> - cond do - stopped_train?(prediction) -> - Content.Message.StoppedTrain.from_prediction(prediction) - - terminal? -> - Content.Message.Predictions.terminal(prediction, station_code, zone) - - true -> - Content.Message.Predictions.non_terminal(prediction, station_code, zone) + if stopped_train?(prediction) do + Content.Message.StoppedTrain.from_prediction(prediction) + else + special_sign = + case sign do + %{pa_ess_loc: "RJFK", text_zone: "m"} -> :jfk_mezzanine + %{pa_ess_loc: "BBOW", text_zone: "e"} -> :bowdoin_eastbound + _ -> nil + end + + Content.Message.Predictions.new(prediction, terminal?, special_sign) end end) - |> Enum.reject(&is_nil(&1)) # Take next two predictions, but if the list has multiple destinations, prefer showing # distinct ones. This helps e.g. the red line trunk where people may need to know about # a particular branch. @@ -74,12 +71,16 @@ defmodule Signs.Utilities.Predictions do end end - def prediction_certainty(prediction, config) do - if config.terminal? || !prediction.seconds_until_arrival do - prediction.departure_certainty - else - prediction.arrival_certainty - end + @spec reverse_prediction?(Predictions.Prediction.t(), boolean()) :: boolean() + def reverse_prediction?(%Predictions.Prediction{} = prediction, terminal?) do + certainty = + if terminal? || !prediction.seconds_until_arrival do + prediction.departure_certainty + else + prediction.arrival_certainty + end + + certainty == @reverse_prediction_certainty end defp get_unique_destination_predictions(predictions, "Green") do diff --git a/test/content/audio/following_train_test.exs b/test/content/audio/following_train_test.exs index 58b586a7..a0ae7104 100644 --- a/test/content/audio/following_train_test.exs +++ b/test/content/audio/following_train_test.exs @@ -18,7 +18,9 @@ defmodule Content.Audio.FollowingTrainTest do destination: :ashmont, prediction: %Predictions.Prediction{route_id: "Mattapan"}, minutes: 5, - terminal?: false + approximate?: false, + terminal?: false, + special_sign: nil } audio = Content.Audio.FollowingTrain.from_predictions_message(message) @@ -38,7 +40,9 @@ defmodule Content.Audio.FollowingTrainTest do destination: :ashmont, prediction: %Predictions.Prediction{route_id: "Mattapan"}, minutes: 5, - terminal?: true + approximate?: false, + terminal?: true, + special_sign: nil } audio = Content.Audio.FollowingTrain.from_predictions_message(message) diff --git a/test/content/audio/next_train_countdown_test.exs b/test/content/audio/next_train_countdown_test.exs index 324efe86..4f64a058 100644 --- a/test/content/audio/next_train_countdown_test.exs +++ b/test/content/audio/next_train_countdown_test.exs @@ -155,8 +155,7 @@ defmodule Content.Audio.NextTrainCountdownTest do minutes: 9, track_number: nil, platform: :braintree, - station_code: "RJFK", - zone: "m" + special_sign: :jfk_mezzanine } assert Content.Audio.to_params(audio) == @@ -189,8 +188,7 @@ defmodule Content.Audio.NextTrainCountdownTest do minutes: 10, track_number: nil, platform: :braintree, - station_code: "RJFK", - zone: "m" + special_sign: :jfk_mezzanine } assert Content.Audio.to_params(audio) == diff --git a/test/content/messages/predictions_test.exs b/test/content/messages/predictions_test.exs index c4c13d81..7c157d28 100644 --- a/test/content/messages/predictions_test.exs +++ b/test/content/messages/predictions_test.exs @@ -1,7 +1,7 @@ defmodule Content.Message.PredictionsTest do use ExUnit.Case, async: true - describe "non_terminal/3" do + describe "non-terminal new/3" do test "puts ARR on the sign when train is 0 seconds away, but not boarding" do prediction = %Predictions.Prediction{ seconds_until_arrival: 0, @@ -12,13 +12,14 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70261" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont ARR" end test "puts BRD on the sign when train is zero stops away" do prediction = %Predictions.Prediction{ + seconds_until_arrival: 0, direction_id: 1, route_id: "Mattapan", destination_stop_id: "70261", @@ -27,7 +28,7 @@ defmodule Content.Message.PredictionsTest do boarding_status: "Boarding" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont BRD" end @@ -42,7 +43,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70275" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Mattapan ARR" end @@ -57,7 +58,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70275" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Mattapan 1 min" end @@ -72,7 +73,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70275" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Mattapan 1 min" end @@ -88,7 +89,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70275" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Mattapan 20+ min" end @@ -103,7 +104,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70261" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont 1 min" end @@ -118,7 +119,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70261" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont 2 min" end @@ -133,13 +134,14 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70261" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont ARR" end test "Shows BRD for negative arrival times if vehicle is STOPPED_AT" do prediction = %Predictions.Prediction{ + seconds_until_arrival: -5, route_id: "Mattapan", direction_id: 1, destination_stop_id: "70261", @@ -148,7 +150,7 @@ defmodule Content.Message.PredictionsTest do boarding_status: "Boarding" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont BRD" end @@ -163,7 +165,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70261" } - msg = Content.Message.Predictions.non_terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, false, nil) assert Content.Message.to_string(msg) == "Ashmont 2 min" end @@ -178,7 +180,7 @@ defmodule Content.Message.PredictionsTest do stops_away: 2 } - msg = Content.Message.Predictions.non_terminal(prediction, "RJFK", "m") + msg = Content.Message.Predictions.new(prediction, false, :jfk_mezzanine) assert Content.Message.to_string(msg) == [ {"Alewife (A) 5 min", 6}, @@ -196,7 +198,7 @@ defmodule Content.Message.PredictionsTest do stops_away: 2 } - msg = Content.Message.Predictions.non_terminal(prediction, "RJFK", "m") + msg = Content.Message.Predictions.new(prediction, false, :jfk_mezzanine) assert Content.Message.to_string(msg) == [ {"Alewife (B) 5 min", 6}, @@ -214,7 +216,7 @@ defmodule Content.Message.PredictionsTest do stops_away: 2 } - msg = Content.Message.Predictions.non_terminal(prediction, "RJFK", "m") + msg = Content.Message.Predictions.new(prediction, false, :jfk_mezzanine) assert Content.Message.to_string(msg) == [ {"Alewife 6 min", 6}, @@ -223,7 +225,7 @@ defmodule Content.Message.PredictionsTest do end end - describe "terminal/3" do + describe "terminal new/3" do test "puts boarding on the sign when train is supposed to be boarding according to rtr" do prediction = %Predictions.Prediction{ seconds_until_departure: 75, @@ -235,7 +237,7 @@ defmodule Content.Message.PredictionsTest do boarding_status: "Stopped at station" } - msg = Content.Message.Predictions.terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, true, nil) assert Content.Message.to_string(msg) == "Ashmont BRD" end @@ -251,7 +253,7 @@ defmodule Content.Message.PredictionsTest do boarding_status: "Stopped at station" } - msg = Content.Message.Predictions.terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, true, nil) assert Content.Message.to_string(msg) == "Ashmont 1 min" end @@ -267,7 +269,7 @@ defmodule Content.Message.PredictionsTest do boarding_status: "Stopped at station" } - msg = Content.Message.Predictions.terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, true, nil) assert Content.Message.to_string(msg) == "Ashmont 2 min" end @@ -282,7 +284,7 @@ defmodule Content.Message.PredictionsTest do destination_stop_id: "70261" } - msg = Content.Message.Predictions.terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, true, nil) assert Content.Message.to_string(msg) == "Ashmont 1 min" end @@ -297,7 +299,7 @@ defmodule Content.Message.PredictionsTest do stops_away: 0 } - msg = Content.Message.Predictions.terminal(prediction, "test", "m") + msg = Content.Message.Predictions.new(prediction, true, nil) assert Content.Message.to_string(msg) == [ {"Oak Grove 2 min", 6},