-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: rework "static" screen parameters
The central/motivating change here is a change to `screens-config-lib` that removes most audio-related parameters from the per-screen configs, since we'd like them to be the same for all screens of a given type. This creates a `Static` struct to encode/organize "static" parameters for each screen type — including candidate generators, refresh rate, periodic audio readout interval, and now various other audio settings.
- Loading branch information
1 parent
a924faa
commit 7a201f7
Showing
11 changed files
with
358 additions
and
310 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,138 @@ | ||
defmodule Screens.V2.ScreenData.Parameters do | ||
@moduledoc false | ||
|
||
alias Screens.Util | ||
alias Screens.V2.CandidateGenerator | ||
alias Screens.V2.ScreenData.Static | ||
alias Screens.V2.ScreenData.Static.PeriodicAudio | ||
alias ScreensConfig.Screen | ||
|
||
@app_id_to_candidate_generators %{ | ||
bus_eink_v2: CandidateGenerator.BusEink, | ||
bus_shelter_v2: CandidateGenerator.BusShelter, | ||
gl_eink_v2: CandidateGenerator.GlEink, | ||
busway_v2: CandidateGenerator.Busway, | ||
solari_large_v2: CandidateGenerator.SolariLarge, | ||
pre_fare_v2: CandidateGenerator.PreFare, | ||
dup_v2: { | ||
CandidateGenerator.Dup, | ||
%{"new_departures" => CandidateGenerator.DupNew} | ||
@all_times {~T[00:00:00], ~T[23:59:59]} | ||
|
||
@static_params %{ | ||
bus_eink_v2: %Static{ | ||
candidate_generator: CandidateGenerator.BusEink, | ||
refresh_rate: 30 | ||
}, | ||
bus_shelter_v2: %Static{ | ||
audio_active_time: {~T[04:45:00], ~T[01:45:00]}, | ||
candidate_generator: CandidateGenerator.BusShelter, | ||
periodic_audio: %PeriodicAudio{ | ||
day_volume: 1.0, | ||
interval_minutes: 5, | ||
night_time: {~T[21:00:00], ~T[07:00:00]}, | ||
night_volume: 0.5 | ||
}, | ||
refresh_rate: 20 | ||
}, | ||
busway_v2: %Static{ | ||
audio_active_time: @all_times, | ||
candidate_generator: CandidateGenerator.Busway, | ||
refresh_rate: 15 | ||
}, | ||
dup_v2: %Static{ | ||
candidate_generator: CandidateGenerator.Dup, | ||
refresh_rate: 30, | ||
variants: %{"new_departures" => CandidateGenerator.DupNew} | ||
}, | ||
elevator_v2: %Static{ | ||
candidate_generator: CandidateGenerator.Elevator, | ||
refresh_rate: 30 | ||
}, | ||
gl_eink_v2: %Static{ | ||
audio_active_time: @all_times, | ||
candidate_generator: CandidateGenerator.GlEink, | ||
refresh_rate: 30 | ||
}, | ||
pre_fare_v2: %Static{ | ||
audio_active_time: {~T[04:45:00], ~T[01:45:00]}, | ||
candidate_generator: CandidateGenerator.PreFare, | ||
refresh_rate: 20 | ||
}, | ||
elevator_v2: CandidateGenerator.Elevator | ||
solari_large_v2: %Static{ | ||
candidate_generator: CandidateGenerator.SolariLarge, | ||
refresh_rate: 15 | ||
} | ||
} | ||
|
||
@app_id_to_refresh_rate %{ | ||
bus_eink_v2: 30, | ||
bus_shelter_v2: 20, | ||
gl_eink_v2: 30, | ||
busway_v2: 15, | ||
solari_large_v2: 15, | ||
pre_fare_v2: 20, | ||
dup_v2: 30, | ||
elevator_v2: 30 | ||
} | ||
@typep static_params :: %{Screen.app_id() => Static.t()} | ||
|
||
@app_id_to_audio_readout_interval %{ | ||
bus_eink_v2: 0, | ||
bus_shelter_v2: 5, | ||
gl_eink_v2: 0, | ||
busway_v2: 0, | ||
solari_large_v2: 0, | ||
pre_fare_v2: 0, | ||
dup_v2: 0, | ||
elevator_v2: 0 | ||
} | ||
@spec audio_enabled?(Screen.t(), DateTime.t()) :: boolean() | ||
@spec audio_enabled?(Screen.t(), DateTime.t(), static_params()) :: boolean() | ||
def audio_enabled?(%Screen{app_id: app_id}, now, static_params \\ @static_params) do | ||
case Map.fetch!(static_params, app_id) do | ||
%Static{audio_active_time: nil} -> | ||
false | ||
|
||
@callback get_candidate_generator(ScreensConfig.Screen.t()) :: module() | ||
@callback get_candidate_generator(ScreensConfig.Screen.t(), String.t() | nil) :: module() | ||
def get_candidate_generator(%ScreensConfig.Screen{app_id: app_id}, variant \\ nil) do | ||
case Map.get(@app_id_to_candidate_generators, app_id) do | ||
{default, _variants} when is_nil(variant) -> default | ||
{_default, variants} -> Map.fetch!(variants, variant) | ||
default -> default | ||
%Static{audio_active_time: {start_time, end_time}} -> | ||
Util.time_in_range?(now, start_time, end_time) | ||
end | ||
end | ||
|
||
@callback get_variants(ScreensConfig.Screen.t()) :: [String.t()] | ||
def get_variants(%ScreensConfig.Screen{app_id: app_id}) do | ||
case Map.get(@app_id_to_candidate_generators, app_id) do | ||
{_default, variants} -> Map.keys(variants) | ||
_default -> [] | ||
@spec audio_interval_minutes(Screen.app_id()) :: pos_integer() | nil | ||
@spec audio_interval_minutes(Screen.app_id(), static_params()) :: pos_integer() | nil | ||
def audio_interval_minutes(app_id, static_params \\ @static_params) do | ||
case Map.fetch!(static_params, app_id) do | ||
%Static{periodic_audio: nil} -> nil | ||
%Static{periodic_audio: %PeriodicAudio{interval_minutes: interval}} -> interval | ||
end | ||
end | ||
|
||
@callback get_refresh_rate(ScreensConfig.Screen.t() | atom()) :: pos_integer() | nil | ||
def get_refresh_rate(%ScreensConfig.Screen{app_id: app_id}) do | ||
get_refresh_rate(app_id) | ||
@spec audio_interval_offset_seconds(Screen.t()) :: pos_integer() | nil | ||
def audio_interval_offset_seconds(%Screen{ | ||
app_params: %ScreensConfig.V2.BusShelter{ | ||
audio: %ScreensConfig.V2.Audio{interval_offset_seconds: interval_offset_seconds} | ||
} | ||
}) do | ||
interval_offset_seconds | ||
end | ||
|
||
def get_refresh_rate(app_id) do | ||
Map.get(@app_id_to_refresh_rate, app_id) | ||
end | ||
def audio_interval_offset_seconds(_screen), do: nil | ||
|
||
@spec audio_volume(Screen.t(), DateTime.t()) :: float() | nil | ||
@spec audio_volume(Screen.t(), DateTime.t(), static_params()) :: float() | nil | ||
def audio_volume(%Screen{app_id: app_id}, now, static_params \\ @static_params) do | ||
case Map.fetch!(static_params, app_id) do | ||
%Static{periodic_audio: nil} -> | ||
nil | ||
|
||
@spec get_audio_readout_interval(ScreensConfig.Screen.t() | atom()) :: pos_integer() | nil | ||
def get_audio_readout_interval(%ScreensConfig.Screen{app_id: app_id}) do | ||
get_refresh_rate(app_id) | ||
%Static{ | ||
periodic_audio: %PeriodicAudio{ | ||
day_volume: day_volume, | ||
night_time: {night_start, night_end}, | ||
night_volume: night_volume | ||
} | ||
} -> | ||
{:ok, now} = DateTime.shift_zone(now, "America/New_York") | ||
if Util.time_in_range?(now, night_start, night_end), do: night_volume, else: day_volume | ||
end | ||
end | ||
|
||
def get_audio_readout_interval(app_id) do | ||
Map.get(@app_id_to_audio_readout_interval, app_id) | ||
@callback candidate_generator(Screen.t()) :: module() | ||
@callback candidate_generator(Screen.t(), String.t() | nil) :: module() | ||
@callback candidate_generator(Screen.t(), String.t() | nil, static_params()) :: module() | ||
def candidate_generator( | ||
%Screen{app_id: app_id}, | ||
variant \\ nil, | ||
static_params \\ @static_params | ||
) do | ||
case Map.fetch!(static_params, app_id) do | ||
%Static{candidate_generator: default} when is_nil(variant) -> default | ||
%Static{variants: %{^variant => variant}} -> variant | ||
end | ||
end | ||
|
||
@spec get_audio_interval_offset_seconds(ScreensConfig.Screen.t()) :: pos_integer() | ||
def get_audio_interval_offset_seconds(%ScreensConfig.Screen{ | ||
app_params: %ScreensConfig.V2.BusShelter{ | ||
audio: %ScreensConfig.V2.Audio{interval_offset_seconds: interval_offset_seconds} | ||
} | ||
}) do | ||
interval_offset_seconds | ||
@callback refresh_rate(Screen.app_id()) :: pos_integer() | nil | ||
@callback refresh_rate(Screen.app_id(), static_params()) :: pos_integer() | nil | ||
def refresh_rate(app_id, static_params \\ @static_params) do | ||
%Static{refresh_rate: refresh_rate} = Map.fetch!(static_params, app_id) | ||
refresh_rate | ||
end | ||
|
||
def get_audio_interval_offset_seconds(_screen), do: 0 | ||
@callback variants(Screen.t()) :: [String.t()] | ||
@callback variants(Screen.t(), static_params()) :: [String.t()] | ||
def variants(%Screen{app_id: app_id}, static_params \\ @static_params) do | ||
%Static{variants: variants} = Map.fetch!(static_params, app_id) | ||
Map.keys(variants) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
defmodule Screens.V2.ScreenData.Static do | ||
@moduledoc "Encodes static configuration that is the same across all screens of a given type." | ||
|
||
@type time_range :: {start :: Time.t(), stop :: Time.t()} | ||
|
||
defmodule PeriodicAudio do | ||
@moduledoc """ | ||
Static configuration for screens that read out audio periodically, without rider action. | ||
Currently, screens that support periodic readouts happen to also be the only screens where we | ||
can control the volume of readouts, so this configuration is bundled together. | ||
""" | ||
|
||
alias Screens.V2.ScreenData.Static | ||
|
||
@type t :: %__MODULE__{ | ||
day_volume: float(), | ||
interval_minutes: pos_integer(), | ||
night_time: Static.time_range(), | ||
night_volume: float() | ||
} | ||
|
||
@enforce_keys ~w[day_volume interval_minutes night_time night_volume]a | ||
defstruct @enforce_keys | ||
end | ||
|
||
@type t :: %__MODULE__{ | ||
audio_active_time: time_range() | nil, | ||
candidate_generator: module(), | ||
periodic_audio: PeriodicAudio.t() | nil, | ||
refresh_rate: pos_integer(), | ||
variants: %{String.t() => module()} | ||
} | ||
|
||
@enforce_keys ~w[candidate_generator refresh_rate]a | ||
defstruct @enforce_keys ++ [audio_active_time: nil, periodic_audio: nil, variants: %{}] | ||
end |
Oops, something went wrong.