Current section
Files
Jump to
Current section
Files
lib/jeff/bus.ex
defmodule Jeff.Bus do
@moduledoc false
alias Jeff.Device
defstruct registry: %{},
command: nil,
reply: nil,
cursor: nil,
poll: [],
conn: nil,
controlling_process: nil
@type t :: %__MODULE__{}
@spec new(keyword()) :: t()
def new(_opts \\ []) do
%__MODULE__{}
end
@spec add_device(t(), keyword()) :: t()
def add_device(bus, opts \\ []) do
device = Device.new(opts)
_bus = register(bus, device.address, device)
end
@spec remove_device(t(), byte()) :: t()
def remove_device(%{poll: poll, registry: registry} = bus, address) do
registry = Map.delete(registry, address)
poll = Enum.reject(poll, &(&1 == address))
%{bus | poll: poll, registry: registry}
end
@spec get_device(t(), byte()) :: Device.t()
def get_device(%{registry: registry}, address) do
Map.fetch!(registry, address)
end
@spec registered?(t(), byte()) :: boolean()
def registered?(%{registry: registry}, address) do
is_map_key(registry, address)
end
@spec put_device(t(), Device.t()) :: t()
def put_device(%{cursor: cursor} = bus, device) do
register(bus, cursor, device)
end
@spec put_device(t(), byte(), Device.t()) :: t()
def put_device(bus, address, device) do
register(bus, address, device)
end
@spec current_device(%__MODULE__{cursor: byte(), registry: map()}) :: Device.t()
def current_device(%{cursor: cursor} = bus) do
get_device(bus, cursor)
end
defp register(%{registry: registry} = bus, address, device) do
registry = Map.put(registry, address, device)
%{bus | registry: registry}
end
defp register(%{cursor: cursor} = bus, device) do
_bus = register(bus, cursor, device)
end
@spec tick(t()) :: t()
def tick(%{cursor: nil, poll: []} = bus) do
poll = addresses(bus)
%{bus | poll: poll}
end
def tick(%{cursor: nil, poll: poll} = bus) do
[cursor | poll] = poll
%{bus | cursor: cursor, poll: poll}
end
def tick(%{command: nil} = bus) do
{device, command} = current_device(bus) |> Device.next_command()
bus = register(bus, device)
%{bus | command: command}
end
# wait for reply
def tick(%{reply: nil} = bus) do
bus
end
# handle reply and reset
def tick(%{reply: _reply} = bus) do
bus = maybe_validate_reply(bus)
# TODO: Convert sleep to part of functional core
if bus.poll == [], do: Process.sleep(100)
%{bus | command: nil, cursor: nil, reply: nil}
end
defp maybe_validate_reply(%{reply: :timeout} = bus), do: bus
defp maybe_validate_reply(bus) do
device = current_device(bus) |> Device.receive_valid_reply()
register(bus, device)
end
@spec send_command(t(), Jeff.Command.t()) :: t()
def send_command(bus, %{address: address} = command) do
device = get_device(bus, address) |> Device.send_command(command)
register(bus, address, device)
end
@spec receive_reply(t(), Jeff.Reply.t()) :: t()
def receive_reply(bus, reply) do
%{bus | reply: reply}
end
defp addresses(bus) do
Map.keys(bus.registry)
end
end