Current section

Files

Jump to
nop_elixir lib nop element fbe.ex
Raw

lib/nop/element/fbe.ex

# ---
# Excerpted from "Programming Elixir ≥ 1.6",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/elixir16 for more book information.
# ---
defmodule NOP.Element.FBE do
defmacro __using__([]) do
quote do
use GenServer
use NOP.Element.ElementBase
require Logger
@attr_prefix "@"
def start_link(fbe_name) do
state = init_state(fbe_name)
state = reset_statistics_to_state(state)
state = set_name(state, fbe_name)
#GenServer.start_link(__MODULE__, state, name: String.to_atom(fbe_name))
GenServer.start_link(__MODULE__, state)
end
def init(state) do
{:ok, state}
end
#Agregate FBE
def handle_call({:get_attr, {fbe_name, attr_name}}, _from, state) do
fbe = get_attribute(get_attributes_from_state(state), fbe_name)
attr_value = GenServer.call(fbe, {:get_attr, attr_name}, :infinity)
{:reply, attr_value, state}
end
def handle_call({:get_attr, attr_name}, _from, state) do
{:reply, get_attribute(get_attributes_from_state(state), attr_name), state}
end
def handle_call(:get_name, _from, state) do
{:reply, get_name(state), state}
end
#Agregate FBE
def handle_cast({:set_attr, {fbe_name, attr_name}, attr_value}, state) do
fbe_attrs = get_attributes_from_state(state)
fbe = get_attribute(get_attributes_from_state(state), fbe_name)
GenServer.cast(fbe, {:set_attr, attr_name, attr_value})
{:noreply, state}
end
#Agregate FBE
def handle_cast({:set_attr_expr, {fbe_name, attr_name}, expression}, state) do
fbe_attrs = get_attributes_from_state(state)
fbe = get_attribute(get_attributes_from_state(state), fbe_name)
GenServer.cast(fbe, {:set_attr_expr, attr_name, expression})
{:noreply, state}
end
defp convert_expression_int([], _fbe_attrs, expression) do
#End of recursion
expression
end
defp covert_attribute_to_string(attr_value) when is_atom(attr_value) do
":" <> to_string(attr_value)
end
defp covert_attribute_to_string(attr_value) when is_binary(attr_value) do
"\"" <> to_string(attr_value) <> "\""
end
defp covert_attribute_to_string(attr_value) do
to_string(attr_value)
end
defp convert_expression_int([token | token_list], fbe_attrs, expression) do
if String.at(token,0) == @attr_prefix do
#Attribute atom to string
attr_name = String.slice(token, 1, String.length(token))
#Attribute value to string
attr_value = get_attribute(fbe_attrs, String.to_atom(attr_name))
value_string = covert_attribute_to_string(attr_value)
#Replace attribute name by value in expression
expr_conv = String.replace(expression, token, value_string)
#Call recursive with expression changed
convert_expression_int(token_list, fbe_attrs, expr_conv)
else
#Call recursive without expression modification
convert_expression_int(token_list, fbe_attrs, expression)
end
end
def handle_cast({:set_attr_expr, attr_name, expression}, state) do
fbe_attrs = get_attributes_from_state(state)
token_list = String.split(expression)
#Convert expression
expr_conv = convert_expression_int(token_list, fbe_attrs, expression)
#Evaluate String
{attr_value, error} = Code.eval_string(expr_conv)
#Set changed value
fbe_attrs = set_attribute(fbe_attrs, attr_name, attr_value, state)
{run_time, state} = :timer.tc(
fn ->
set_attributes_to_state(state, fbe_attrs)
end
)
state = add_statistics_to_state(state, div(run_time, 1000))
{:noreply, state}
end
def handle_cast({:set_attr, attr_name, attr_value}, state) do
fbe_attrs = get_attributes_from_state(state)
fbe_attrs = set_attribute(fbe_attrs, attr_name, attr_value, state)
{run_time, state} = :timer.tc(
fn ->
set_attributes_to_state(state, fbe_attrs)
end
)
state = add_statistics_to_state(state, div(run_time, 1000))
{:noreply, state}
end
#Agregate FBE
def handle_cast({:link_premise, {fbe_name, attr_name}, premise, side, premise_name}, state) do
fbe_attrs = get_attributes_from_state(state)
fbe = get_attribute(get_attributes_from_state(state), fbe_name)
GenServer.cast(fbe, {:link_premise, attr_name, premise, side, premise_name})
{:noreply, state}
end
def handle_cast({:link_premise, attr_name, premise, side, premise_name}, state) do
state = link_premise_to_attribute(state, attr_name, premise, side, premise_name)
{:noreply, state}
end
defp init_state(fbe_name) do
# int_attributes needs to be defined!
fbe_attrs = int_attributes()
%{:attributes => fbe_attrs, :links => %{}}
end
defp get_attributes_from_state(state) do
Map.get(state, :attributes)
end
defp set_attributes_to_state(state, fbe_attrs) do
Map.put(state, :attributes, fbe_attrs)
end
defp get_attribute(fbe_attrs, attr_name) do
Map.get(fbe_attrs, attr_name)
end
defp notify_premises_int([], _, _, _) do
# No premises to notify
end
defp notify_premises_int(nil, _, _, _) do
# No premises to notify
end
defp notify_premises_int([{premise, side} | premises], attr_name, attr_value, fbe_name) do
GenServer.cast(premise, {:notify, side, self(), attr_name, attr_value, fbe_name})
notify_premises_int(premises, attr_name, attr_value, fbe_name)
end
defp notify_premises(state, attr_name, attr_value, fbe_name) do
links = get_links(state)
premises = Map.get(links, attr_name)
notify_premises_int(premises, attr_name, attr_value, fbe_name)
end
defp set_attribute(fbe_attrs, attr_name, attr_value, state) do
# Get Old Value
old_value = get_attribute(fbe_attrs, attr_name)
if old_value != attr_value do
# Get Name
fbe_name = get_name(state)
Logger.debug(
"FBE attribute #{fbe_name}.#{attr_name} changed from #{old_value} to #{attr_value}"
)
notify_premises(state, attr_name, attr_value, fbe_name)
Map.put(fbe_attrs, attr_name, attr_value)
else
fbe_attrs
end
end
defp get_links(state) do
Map.get(state, :links)
end
defp set_links(state, links) do
Map.put(state, :links, links)
end
defp link_premise_to_attribute(state, attr_name, premise, side, premise_name) do
Logger.debug(
"FBE attribute #{get_name(state)}.#{attr_name} linked to premise #{premise_name} on #{
side
} side"
)
links = get_links(state)
premises = Map.get(links, attr_name)
premises =
if premises == nil do
[]
else
premises
end
premises = premises ++ [{premise, side}]
links = Map.put(links, attr_name, premises)
set_links(state, links)
end
end
end
end