ex_speed_game/lib/game/lights.ex

115 lines
2.9 KiB
Elixir

defmodule ExSpeedGame.Game.Lights do
use GenServer
use Bitwise, only_operators: true
alias ExSpeedGame.Game.Types
@type values :: {boolean(), boolean(), boolean(), boolean()}
defmodule Options do
@type t :: %__MODULE__{
light_pins: Types.led_pins(),
name: GenServer.name()
}
@enforce_keys [:light_pins, :name]
defstruct [:light_pins, :name]
end
defmodule State do
@type t :: %__MODULE__{
lights: {Types.pin(), Types.pin(), Types.pin(), Types.pin()},
references: [reference()]
}
defstruct [:lights, :references]
end
### SERVER INTERFACE
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
def start_link(%Options{} = opts) do
{{pin1, _}, {pin2, _}, {pin3, _}, {pin4, _}} = opts.light_pins
GenServer.start_link(
__MODULE__,
%{pins: {pin1, pin2, pin3, pin4}},
name: opts.name
)
end
@impl true
@spec init(%{pins: {Types.pin(), Types.pin(), Types.pin(), Types.pin()}}) :: {:ok, State.t()}
def init(%{pins: pins}) do
references =
for pin <- Tuple.to_list(pins) do
{:ok, ref} = Circuits.GPIO.open(pin, :output, initial_value: 0)
ref
end
{:ok, %State{lights: pins, references: references}}
end
@impl true
@spec handle_call(
{:set_lights, values()},
GenServer.from(),
State.t()
) :: {:reply, :ok, State.t()}
def handle_call({:set_lights, values}, _from, %State{references: references} = state) do
references
|> Enum.with_index()
|> Enum.each(fn {ref, i} ->
Circuits.GPIO.write(ref, elem(values, i) |> bool2val())
end)
{:reply, :ok, state}
end
@spec bool2val(boolean()) :: Circuits.GPIO.value()
defp bool2val(bool)
defp bool2val(true), do: 1
defp bool2val(false), do: 0
### CLIENT INTERFACE
@doc """
Set the lights on/off according to the given value.
"""
@spec set(GenServer.name(), values()) :: :ok | no_return()
def set(server, values) do
:ok = GenServer.call(server, {:set_lights, values})
end
@doc """
Turn off all lights.
"""
@spec clear(GenServer.name()) :: :ok | no_return()
def clear(server) do
set(server, {false, false, false, false})
end
@doc """
Show the given number on the lights as a binary pattern.
"""
@spec show_binary(GenServer.name(), integer()) :: :ok | no_return()
def show_binary(server, number) do
values = number2binary(number)
set(server, values)
end
@doc """
Set the given index on and the others off.
"""
@spec set_index(GenServer.name(), integer()) :: :ok | no_return()
def set_index(server, index) do
set(server, {index == 1, index == 2, index == 3, index == 4})
end
@spec number2binary(integer()) :: values()
defp number2binary(number) do
{
(number &&& 0b1000) != 0,
(number &&& 0b0100) != 0,
(number &&& 0b0010) != 0,
(number &&& 0b0001) != 0
}
end
end