Skip to content

Commit

Permalink
feat: signs can have a "default mode"
Browse files Browse the repository at this point in the history
Previously, if a sign wasn't configured in SignsUI, it would always be
in the `off` mode. We'd like to be able to configure our new lab signs
for testing without the complexity of supporting this unique "kind" of
sign in SignsUI as well as this project.

This adds an optional `default_mode` field to sign configuration, which
can be set to any of the "modes" supported by SignsUI. For now, this is
only supported by `Realtime` (a.k.a. subway) signs and not `Bus` signs,
but this would be easy enough to extend later if needed.

Due to now needing a sign's default mode as well as its ID whenever we
want to determine its "current mode", this includes a refactor of the
previously-added `current_config` logging so the value is determined
when a message is enqueued, instead of when it is later processed.
  • Loading branch information
digitalcora committed Jul 22, 2024
1 parent 4c86c2e commit 7ef9e24
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 89 deletions.
10 changes: 5 additions & 5 deletions lib/engine/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ defmodule Engine.Config do
end

@impl true
def sign_config(table_name \\ @table_signs, sign_id) do
case :ets.lookup(table_name, sign_id) do
[{^sign_id, config}] -> config
_ -> :off
def sign_config(table \\ @table_signs, sign_id, default) do
case :ets.lookup(table, sign_id) do
[{^sign_id, config}] when not is_nil(config) -> config
_ -> default
end
end

Expand Down Expand Up @@ -200,7 +200,7 @@ defmodule Engine.Config do
:temporary_terminal

true ->
:off
nil
end
end

Expand Down
3 changes: 2 additions & 1 deletion lib/engine/config_api.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Engine.ConfigAPI do
@callback sign_config(String.t()) :: Engine.Config.sign_config()
@callback sign_config(id :: String.t(), default :: Engine.Config.sign_config()) ::
Engine.Config.sign_config()
@callback headway_config(String.t(), DateTime.t()) :: Engine.Config.Headway.t() | nil
@callback scu_migrated?(String.t()) :: boolean()
end
11 changes: 5 additions & 6 deletions lib/message_queue.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ defmodule MessageQueue do
Content.Message.value(),
integer(),
integer() | :now,
String.t()
keyword()
) :: :ok
def update_sign(pid \\ __MODULE__, text_id, top_line, bottom_line, duration, start, sign_id) do
def update_sign(pid \\ __MODULE__, text_id, top_line, bottom_line, duration, start, log_meta) do
GenServer.call(
pid,
{:queue_update, {:update_sign, [text_id, top_line, bottom_line, duration, start, sign_id]}}
{:queue_update, {:update_sign, [text_id, top_line, bottom_line, duration, start, log_meta]}}
)
end

Expand All @@ -54,13 +54,12 @@ defmodule MessageQueue do
[Content.Audio.value()],
integer(),
integer(),
String.t(),
[keyword()]
) :: :ok
def send_audio(pid \\ __MODULE__, audio_id, audios, priority, timeout, sign_id, extra_logs) do
def send_audio(pid \\ __MODULE__, audio_id, audios, priority, timeout, log_metas) do
GenServer.call(
pid,
{:queue_update, {:send_audio, [audio_id, audios, priority, timeout, sign_id, extra_logs]}}
{:queue_update, {:send_audio, [audio_id, audios, priority, timeout, log_metas]}}
)
end

Expand Down
45 changes: 17 additions & 28 deletions lib/pa_ess/http_updater.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ defmodule PaEss.HttpUpdater do
end

def process(
{:update_sign, [{station, zone}, top_line, bottom_line, duration, start_secs, sign_id]},
{:update_sign, [{station, zone}, top_line, bottom_line, duration, start_secs, log_meta]},
state
) do
top_cmd = to_command(top_line, duration, start_secs, zone, 1)
Expand All @@ -78,29 +78,23 @@ defmodule PaEss.HttpUpdater do

{arinc_ms, signs_ui_ms, result} = send_payload(encoded, state)

log("update_sign", encoded,
arinc_ms: arinc_ms,
signs_ui_ms: signs_ui_ms,
top_line: inspect(top_line),
bottom_line: inspect(bottom_line),
sign_id: sign_id,
current_config:
case Engine.Config.sign_config(sign_id) do
type when is_atom(type) -> type
tuple when is_tuple(tuple) -> elem(tuple, 0)
_ -> nil
end
log(
"update_sign",
encoded,
[
arinc_ms: arinc_ms,
signs_ui_ms: signs_ui_ms,
top_line: inspect(top_line),
bottom_line: inspect(bottom_line)
] ++ log_meta
)

result
end

def process(
{:send_audio, [{station, zones}, audios, priority, timeout, sign_id, extra_logs]},
state
) do
for {audio, extra_logs} <- Enum.zip(audios, extra_logs) do
process_send_audio(station, zones, audio, priority, timeout, sign_id, extra_logs, state)
def process({:send_audio, [{station, zones}, audios, priority, timeout, log_metas]}, state) do
for {audio, log_meta} <- Enum.zip(audios, log_metas) do
process_send_audio(station, zones, audio, priority, timeout, log_meta, state)
end
|> List.last()
end
Expand All @@ -111,12 +105,11 @@ defmodule PaEss.HttpUpdater do
Content.Audio.value(),
integer(),
integer(),
String.t(),
list,
keyword(),
t()
) ::
post_result()
defp process_send_audio(station, zones, audio, priority, timeout, sign_id, extra_logs, state) do
defp process_send_audio(station, zones, audio, priority, timeout, log_meta, state) do
case audio do
{:canned, {message_id, vars, type}} ->
encoded =
Expand All @@ -134,11 +127,7 @@ defmodule PaEss.HttpUpdater do

{arinc_ms, signs_ui_ms, result} = send_payload(encoded, state)

log(
"send_audio",
encoded,
[arinc_ms: arinc_ms, signs_ui_ms: signs_ui_ms, sign_id: sign_id] ++ extra_logs
)
log("send_audio", encoded, [arinc_ms: arinc_ms, signs_ui_ms: signs_ui_ms] ++ log_meta)

result

Expand All @@ -160,7 +149,7 @@ defmodule PaEss.HttpUpdater do
log(
"send_custom_audio",
encoded,
[arinc_ms: arinc_ms, signs_ui_ms: signs_ui_ms, sign_id: sign_id] ++ extra_logs
[arinc_ms: arinc_ms, signs_ui_ms: signs_ui_ms] ++ log_meta
)

result
Expand Down
26 changes: 19 additions & 7 deletions lib/pa_ess/updater.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ defmodule PaEss.Updater do
scu_id: scu_id,
pa_ess_loc: pa_ess_loc,
text_zone: text_zone,
default_mode: default_mode,
config_engine: config_engine
},
top,
bottom
) do
log_config =
case config_engine.sign_config(id, default_mode) do
mode when is_atom(mode) -> mode
mode when is_tuple(mode) -> elem(mode, 0)
_ -> nil
end

log_meta = [sign_id: id, current_config: log_config]

if config_engine.scu_migrated?(scu_id) do
pages = zip_pages(top, bottom)

Expand All @@ -26,10 +36,10 @@ defmodule PaEss.Updater do
visual_data: format_pages(pages),
expiration: 180,
tag: nil
}, [sign_id: id, visual: inspect(pages)]}
}, [visual: inspect(pages)] ++ log_meta}
)
else
MessageQueue.update_sign({pa_ess_loc, text_zone}, top, bottom, 180, :now, id)
MessageQueue.update_sign({pa_ess_loc, text_zone}, top, bottom, 180, :now, log_meta)
end
end

Expand All @@ -44,8 +54,10 @@ defmodule PaEss.Updater do
},
audios,
tts_audios,
extra_logs
log_metas
) do
log_metas = Enum.map(log_metas, fn log_meta -> [{:sign_id, id} | log_meta] end)

if config_engine.scu_migrated?(scu_id) do
Task.Supervisor.start_child(PaEss.TaskSupervisor, fn ->
files =
Expand All @@ -54,8 +66,8 @@ defmodule PaEss.Updater do
end)
|> Task.await_many()

Enum.zip([files, tts_audios, extra_logs])
|> Enum.each(fn {file, {text, pages}, logs} ->
Enum.zip([files, tts_audios, log_metas])
|> Enum.each(fn {file, {text, pages}, log_meta} ->
PaEss.ScuQueue.enqueue_message(
scu_id,
{:message, scu_id,
Expand All @@ -66,12 +78,12 @@ defmodule PaEss.Updater do
audio_data: [Base.encode64(file)],
expiration: 30,
tag: nil
}, [sign_id: id, audio: inspect(text), visual: inspect(pages)] ++ logs}
}, [audio: inspect(text), visual: inspect(pages)] ++ log_meta}
)
end)
end)
else
MessageQueue.send_audio({pa_ess_loc, audio_zones}, audios, 5, 60, id, extra_logs)
MessageQueue.send_audio({pa_ess_loc, audio_zones}, audios, 5, 60, log_metas)
end
end

Expand Down
6 changes: 4 additions & 2 deletions lib/signs/bus.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ defmodule Signs.Bus do
:last_read_time,
:pa_message_plays
]
defstruct @enforce_keys
defstruct @enforce_keys ++ [default_mode: :off]

@type t :: %__MODULE__{
id: String.t(),
Expand All @@ -47,6 +47,7 @@ defmodule Signs.Bus do
text_zone: String.t(),
audio_zones: [String.t()],
max_minutes: integer(),
default_mode: Engine.Config.sign_config(),
configs: list(),
top_configs: list(),
bottom_configs: list(),
Expand Down Expand Up @@ -149,6 +150,7 @@ defmodule Signs.Bus do

%__MODULE__{
id: id,
default_mode: default_mode,
configs: configs,
config_engine: config_engine,
prediction_engine: prediction_engine,
Expand All @@ -158,7 +160,7 @@ defmodule Signs.Bus do
} = state

# Fetch the data we need to compute the updated sign content.
config = config_engine.sign_config(id)
config = config_engine.sign_config(id, default_mode)
bridge_enabled? = config_engine.chelsea_bridge_config() == :auto
bridge_status = bridge_engine.bridge_status()
current_time = Timex.now()
Expand Down
6 changes: 5 additions & 1 deletion lib/signs/realtime.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ defmodule Signs.Realtime do
announced_stalls: [],
announced_custom_text: nil,
announced_alert: false,
default_mode: :off,
prev_prediction_keys: nil,
prev_predictions: [],
uses_shuttles: true,
Expand All @@ -64,6 +65,7 @@ defmodule Signs.Realtime do
text_zone: String.t(),
audio_zones: [String.t()],
source_config: SourceConfig.config() | {SourceConfig.config(), SourceConfig.config()},
default_mode: Engine.Config.sign_config(),
current_content_top: Content.Message.value(),
current_content_bottom: Content.Message.value(),
prediction_engine: module(),
Expand Down Expand Up @@ -101,6 +103,8 @@ defmodule Signs.Realtime do
text_zone: Map.fetch!(config, "text_zone"),
audio_zones: Map.fetch!(config, "audio_zones"),
source_config: source_config,
default_mode:
config |> Map.get("default_mode") |> then(&if(&1 == "auto", do: :auto, else: :off)),
current_content_top: "",
current_content_bottom: "",
prediction_engine: Engine.Predictions,
Expand Down Expand Up @@ -145,7 +149,7 @@ defmodule Signs.Realtime do
sign_stop_ids = SourceConfig.sign_stop_ids(sign.source_config)
sign_routes = SourceConfig.sign_routes(sign.source_config)
alert_status = sign.alerts_engine.max_stop_status(sign_stop_ids, sign_routes)
sign_config = sign.config_engine.sign_config(sign.id)
sign_config = sign.config_engine.sign_config(sign.id, sign.default_mode)
current_time = sign.current_time_fn.()

first_scheduled_departures =
Expand Down
1 change: 1 addition & 0 deletions priv/signs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"type": "realtime",
"pa_ess_loc": "201",
"scu_id": "TBR01",
"default_mode": "auto",
"read_loop_offset": 120,
"text_zone": "m",
"audio_zones": [
Expand Down
22 changes: 11 additions & 11 deletions test/engine/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ defmodule Engine.ConfigTest do
Engine.Config.update()

Process.sleep(50)
assert Engine.Config.sign_config("chelsea_inbound") == :auto
assert Engine.Config.sign_config("chelsea_inbound", :off) == :auto
end

test "is off when the sign is disabled" do
Engine.Config.update()

Process.sleep(50)
assert Engine.Config.sign_config("chelsea_outbound") == :off
assert Engine.Config.sign_config("chelsea_outbound", :auto) == :off
end

test "is off when the sign is unspecified" do
test "is the provided default value when the sign is unspecified" do
Engine.Config.update()

Process.sleep(50)
assert Engine.Config.sign_config("unspecified_sign") == :off
assert Engine.Config.sign_config("unspecified_sign", :headway) == :headway
end

test "returns custom text when it's not expired" do
Expand All @@ -33,7 +33,7 @@ defmodule Engine.ConfigTest do
end
})

assert Engine.Config.sign_config(state.table_name_signs, "custom_text_test") ==
assert Engine.Config.sign_config(state.table_name_signs, "custom_text_test", :off) ==
{:static_text, {"Test message", "Please ignore"}}
end

Expand All @@ -46,14 +46,14 @@ defmodule Engine.ConfigTest do
end
})

assert Engine.Config.sign_config(state.table_name_signs, "custom_text_test") == :auto
assert Engine.Config.sign_config(state.table_name_signs, "custom_text_test", :off) == :auto
end

test "properly returns headway mode" do
Engine.Config.update()

Process.sleep(50)
assert Engine.Config.sign_config("headway_test") == :headway
assert Engine.Config.sign_config("headway_test", :off) == :headway
end
end

Expand All @@ -66,7 +66,7 @@ defmodule Engine.ConfigTest do
test "handles new format of config" do
initialize_test_state(%{table_name_signs: :test_new_format, current_version: "new_format"})

assert Engine.Config.sign_config(:test_new_format, "some_custom_sign") ==
assert Engine.Config.sign_config(:test_new_format, "some_custom_sign", :off) ==
{:static_text, {"custom", ""}}
end

Expand All @@ -89,19 +89,19 @@ defmodule Engine.ConfigTest do
test "correctly loads config for a sign with a mode of \"off\"" do
initialize_test_state(%{table_name_signs: :config_test_off})

assert Engine.Config.sign_config(:config_test_off, "off_test") == :off
assert Engine.Config.sign_config(:config_test_off, "off_test", :auto) == :off
end

test "correctly loads config for a sign with a mode of \"auto\"" do
initialize_test_state(%{table_name_signs: :config_test_auto})

assert Engine.Config.sign_config(:config_test_auto, "auto_test") == :auto
assert Engine.Config.sign_config(:config_test_auto, "auto_test", :off) == :auto
end

test "correctly loads config for a sign with a mode of \"headway\"" do
initialize_test_state(%{table_name_signs: :config_test_headway})

assert Engine.Config.sign_config(:config_test_headway, "headway_test") == :headway
assert Engine.Config.sign_config(:config_test_headway, "headway_test", :off) == :headway
end

test "loads chelsea bridge config" do
Expand Down
Loading

0 comments on commit 7ef9e24

Please sign in to comment.