Packages
evil_clock
0.1.0
Elixir application for interfacing with Alpha Clock Five from Evil Mad Scientist Laboratories
Current section
Files
Jump to
Current section
Files
lib/server.ex
defmodule EvilClock.Server do
use GenServer
use Timex
require Logger
alias Nerves.UART
alias EvilClock.{Config, Framing}
@default_opts clock: Config.get(:evil_clock, :primary_clock),
scroll_rate: Config.get(:evil_clock, :scroll_rate),
fw_ver: Config.get(:evil_clock, :firmware_version),
points: List.duplicate(:none, 5),
resume_after: 1000
# Server
def start_link([_uart, _port, _speed] = args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
def init([uart, port, speed]) do
Logger.debug("Evil clock server starting")
UART.open(uart, port, speed: speed, active: true)
UART.configure(uart, framing: {UART.Framing.Line, separator: "\r\n"})
{:ok, {uart}}
end
def handle_cast({:display_ascii, ascii, opts}, {uart} = state) when byte_size(ascii) > 5 do
# Push start of scrolling to the right side
padded = String.duplicate(" ", 4) <> ascii
Stream.interval(opts[:scroll_rate])
|> Stream.take_while(fn(offset) -> offset <= byte_size(padded) end)
|> Stream.each(fn(offset) -> write_slice(uart, offset, 5, padded, opts) end)
|> Stream.run
schedule_mode_time(opts)
{:noreply, state}
end
def handle_cast({:display_ascii, ascii, opts}, {uart} = state) when byte_size(ascii) <= 5 do
write_slice(uart, 0, 5, ascii, opts)
schedule_mode_time(opts)
{:noreply, state}
end
def handle_cast({:set_time, timestamp, opts}, {uart} = state) do
framing = Framing.build_set_time_frame(timestamp, opts[:fw_ver])
UART.write(uart, framing)
{:noreply, state}
end
def handle_cast({:mode_time, opts}, {uart} = state) do
handle_mode_time(uart, opts)
{:noreply, state}
end
def handle_info({:mode_time, opts}, {uart} = state) do
handle_mode_time(uart, opts)
{:noreply, state}
end
def handle_info({_app, port, {:error, reason}}, state) do
Logger.error("Error involving clock on #{port}: #{inspect reason}")
{:stop, reason, state}
end
def handle_info({_app, port, msg}, state) do
Logger.debug("Received from clock on #{port}: #{inspect msg} // state: #{inspect state}")
{:noreply, state}
end
defp write_slice(uart, offset, length, text, opts) do
slice = String.slice(text, offset, length) |> String.upcase
framing = Framing.build_ascii_frame(slice, opts[:points], opts[:clock], opts[:fw_ver])
UART.write(uart, framing)
end
defp schedule_mode_time(opts) do
Process.send_after(self(), {:mode_time, opts}, opts[:resume_after])
end
defp handle_mode_time(uart, opts) do
framing = Framing.build_mode_time_frame(opts[:fw_ver])
UART.write(uart, framing)
end
# Client API
def display_ascii(ascii, opts \\ []) do
# TODO: Separate options for different commands
opts = Keyword.merge(@default_opts, opts)
GenServer.cast(__MODULE__, {:display_ascii, ascii, opts})
end
def set_time(timestamp, opts \\ [])
def set_time(timestamp, opts) when is_binary(timestamp) do
opts = Keyword.merge(@default_opts, opts)
GenServer.cast(__MODULE__, {:set_time, timestamp, opts})
end
def set_time(timestamp, opts) when is_integer(timestamp) do
set_time(timestamp |> to_string, opts)
end
def set_time_local(opts \\ []) do
utc_offset = Timezone.local |> Timezone.total_offset
timestamp = Timex.local |> Timex.shift(seconds: utc_offset) |> Timex.to_unix
set_time(timestamp, opts)
end
def mode_time(opts \\ []) do
opts = Keyword.merge(@default_opts, opts)
GenServer.cast(__MODULE__, {:mode_time, opts})
end
end