Skip to content

Commit

Permalink
Merge pull request #33 from WebWideMatrix/basic-battery-support
Browse files Browse the repository at this point in the history
Basic battery support
  • Loading branch information
dibaunaumh authored Oct 11, 2022
2 parents 3e63941 + 27f8054 commit 7cbd724
Show file tree
Hide file tree
Showing 21 changed files with 631 additions and 9 deletions.
1 change: 1 addition & 0 deletions bldg_server/lib/bldg_server/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule BldgServer.Application do
# Starts a worker by calling: BldgServer.Worker.start_link(arg)
# {BldgServer.Worker, arg},
BldgServerWeb.BldgCommandExecutor,
BldgServerWeb.BatteryChatDispatcher,
# Start the http client
{Finch, name: FinchClient}
]
Expand Down
164 changes: 164 additions & 0 deletions bldg_server/lib/bldg_server/batteries.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
defmodule BldgServer.Batteries do
@moduledoc """
The Batteries context.
"""

import Ecto.Query, warn: false
alias BldgServer.Repo

alias BldgServer.Batteries.Battery

@doc """
Returns the list of batteries.
## Examples
iex> list_batteries()
[%Battery{}, ...]
"""
def list_batteries do
Repo.all(Battery)
end


@doc """
Returns the list of attached batteries in a given floor.
## Examples
iex> get_batteries_in_floor(flr)
[%Battery{}, ...]
"""
def get_batteries_in_floor(flr) do
q = from b in Battery, where: b.flr == ^flr and b.is_attached
Repo.all(q)
end





@doc """
Gets a single battery.
Raises `Ecto.NoResultsError` if the Battery does not exist.
## Examples
iex> get_battery!(123)
%Battery{}
iex> get_battery!(456)
** (Ecto.NoResultsError)
"""
def get_battery!(id), do: Repo.get!(Battery, id)



@doc """
Gets a single battery by it's container bldg's url.
Raises `Ecto.NoResultsError` if the Battery does not exist.
## Examples
iex> get_battery_by_bldg_url!("g/bldg_name")
%Battery{}
iex> get_battery_by_bldg_url!("g/bldg_name")
** (Ecto.NoResultsError)
"""
def get_attached_battery_by_bldg_url!(bldg_url) do
clauses = [is_attached: :true, bldg_url: bldg_url]
Repo.get_by!(Battery, clauses)
end

@doc """
Gets a single battery by it's container bldg's address.
Raises `Ecto.NoResultsError` if the Battery does not exist.
## Examples
iex> get_battery_by_bldg_address!("g-b(10,20)")
%Battery{}
iex> get_battery_by_bldg_address!("g-b(30,40)")
** (Ecto.NoResultsError)
"""
def get_attached_battery_by_bldg_address!(bldg_address) do
clauses = [is_attached: :true, bldg_address: bldg_address]
Repo.get_by!(Battery, clauses)
end


@doc """
Creates a battery.
## Examples
iex> create_battery(%{field: value})
{:ok, %Battery{}}
iex> create_battery(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_battery(attrs \\ %{}) do
%Battery{}
|> Battery.changeset(attrs)
|> Repo.insert()
end

@doc """
Updates a battery.
## Examples
iex> update_battery(battery, %{field: new_value})
{:ok, %Battery{}}
iex> update_battery(battery, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_battery(%Battery{} = battery, attrs) do
battery
|> Battery.changeset(attrs)
|> Repo.update()
end

@doc """
Deletes a battery.
## Examples
iex> delete_battery(battery)
{:ok, %Battery{}}
iex> delete_battery(battery)
{:error, %Ecto.Changeset{}}
"""
def delete_battery(%Battery{} = battery) do
Repo.delete(battery)
end

@doc """
Returns an `%Ecto.Changeset{}` for tracking battery changes.
## Examples
iex> change_battery(battery)
%Ecto.Changeset{source: %Battery{}}
"""
def change_battery(%Battery{} = battery) do
Battery.changeset(battery, %{})
end
end
25 changes: 25 additions & 0 deletions bldg_server/lib/bldg_server/batteries/battery.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule BldgServer.Batteries.Battery do
use Ecto.Schema
import Ecto.Changeset

schema "batteries" do
field :battery_type, :string
field :battery_vendor, :string
field :battery_version, :string
field :bldg_url, :string
field :callback_url, :string
field :direct_only, :boolean, default: false
field :flr, :string
field :is_attached, :boolean, default: false

timestamps()
end

@doc false
def changeset(battery, attrs) do
battery
|> cast(attrs, [:bldg_url, :flr, :callback_url, :is_attached, :direct_only, :battery_type, :battery_version, :battery_vendor])
|> validate_required([:bldg_url, :flr, :callback_url])
|> unique_constraint(:single_attached_battery_in_bldg, name: :single_attached_battery_in_bldg)
end
end
39 changes: 38 additions & 1 deletion bldg_server/lib/bldg_server/buildings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,49 @@ Given an entity:
}
"""
def build(entity) do
bldg_params = entity
entity
|> figure_out_flr()
|> figure_out_bldg_url()
|> decide_on_location()
|> calculate_nesting_depth()
|> remove_build_params()
end


# TODO duplicate code, please consolidate
def append_message_to_list(msg_list, msg) do
case msg_list do
nil -> [msg]
_ -> [msg | msg_list]
end
end


# TODO duplicate code, please consolidate
def is_command(msg_text), do: String.at(msg_text, 0) == "/"


# TODO duplicate code, please consolidate
def say(%Bldg{} = bldg, msg) do
{_, text} = msg
|> Map.merge(%{"say_time" => System.system_time(:millisecond)})
|> JSON.encode()

new_prev_messages = append_message_to_list(bldg.previous_messages, text)
changes = %{previous_messages: new_prev_messages}
result = update_bldg(bldg, changes)

# the message may be a command for bldg manipulation, so
# broadcast an event for it, so that the command executor can process it
if is_command(msg["say_text"]) do
BldgServerWeb.Endpoint.broadcast!(
"chat",
"new_message",
msg
)
end

result
end

end
3 changes: 2 additions & 1 deletion bldg_server/lib/bldg_server/buildings/bldg.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ defmodule BldgServer.Buildings.Bldg do
field :flr_url, :string
field :flr_level, :integer
field :nesting_depth, :integer
field :previous_messages, {:array, :string}

timestamps()
end

@doc false
def changeset(bldg, attrs) do
bldg
|> cast(attrs, [:address, :flr, :x, :y, :is_composite, :name, :web_url, :entity_type, :state, :category, :tags, :summary, :picture_url, :data, :owners, :bldg_url, :flr_url, :flr_level, :nesting_depth])
|> cast(attrs, [:address, :flr, :x, :y, :is_composite, :name, :web_url, :entity_type, :state, :category, :tags, :summary, :picture_url, :data, :owners, :bldg_url, :flr_url, :flr_level, :nesting_depth, :previous_messages])
|> validate_required([:bldg_url, :address, :flr, :x, :y, :is_composite, :name, :entity_type, :flr_url, :flr_level, :nesting_depth])
|> unique_constraint(:address)
|> unique_constraint(:bldg_url)
Expand Down
4 changes: 3 additions & 1 deletion bldg_server/lib/bldg_server/residents.ex
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ defmodule BldgServer.Residents do


def say(%Resident{} = resident, msg) do
{_, text} = JSON.encode(msg)
{_, text} = msg
|> Map.merge(%{"say_time" => System.system_time(:millisecond)})
|> JSON.encode()

new_prev_messages = append_message_to_list(resident.previous_messages, text)
changes = %{previous_messages: new_prev_messages}
Expand Down
53 changes: 53 additions & 0 deletions bldg_server/lib/bldg_server_web/battery_chat_dispatcher.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule BldgServerWeb.BatteryChatDispatcher do
use GenServer
require Logger
alias BldgServer.PubSub

alias BldgServer.Batteries


def start_link(_) do
GenServer.start_link(__MODULE__, name: __MODULE__)
end

def init(_) do
Phoenix.PubSub.subscribe(PubSub, "chat")
IO.puts("~~~~~~~~~~~ [battery chat dispatcher] subscribed to chat")
{:ok, %{}}
end

def handle_call(:get, _, state) do
{:reply, state, state}
end


def send_message_to_battery(callback_url, msg) do
Logger.info("~~~~~ About to invoke battery callback URL at: #{callback_url}")
header_key = "content-type"
header_val = "application/json"
{_, msg_json} = Jason.encode(msg)
IO.inspect(msg_json)
Finch.build(:post, callback_url, [{header_key, header_val}], msg_json)
|> Finch.request(FinchClient)
|> IO.inspect()
end


#def handle_info({sender, message, flr}, state) do
def handle_info(%{event: "new_message", payload: new_message}, state) do
#Logger.info("chat message received at #{flr} from #{sender}: #{message}")
Logger.info("~~~~~~~~~~~ [battery chat dispatcher] chat message received: #{new_message["message"]}")

# query for all batteries inside that message flr
# & invoke the callback url per each battery, with the message details in the body

batteries = new_message["say_flr"]
|> Batteries.get_batteries_in_floor()

IO.inspect(batteries)

Enum.map(batteries, fn b -> send_message_to_battery(b.callback_url, new_message) end)

{:noreply, state}
end
end
4 changes: 2 additions & 2 deletions bldg_server/lib/bldg_server_web/bldg_command_executor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule BldgServerWeb.BldgCommandExecutor do

def init(_) do
Phoenix.PubSub.subscribe(PubSub, "chat")
IO.puts("subscribed")
IO.puts("~~~~~~~~~~~~ [bldg command executor] subscribed to chat")
{:ok, %{}}
end

Expand Down Expand Up @@ -415,7 +415,7 @@ defmodule BldgServerWeb.BldgCommandExecutor do
#def handle_info({sender, message, flr}, state) do
def handle_info(%{event: "new_message", payload: new_message}, state) do
#Logger.info("chat message received at #{flr} from #{sender}: #{message}")
Logger.info("chat message received: #{new_message["message"]}")
Logger.info("~~~~~~~~~~~~ [bldg command executor] chat message received: #{new_message["message"]}")

new_message["say_text"]
|> parse_command()
Expand Down
Loading

0 comments on commit 7cbd724

Please sign in to comment.