# --- # 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 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 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 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) #beg_time = get_timestamp() #state = set_attributes_to_state(state, fbe_attrs) # end_time = get_timestamp() #state = add_statistics_to_state(state, diff_between_timestamp(beg_time,end_time)) {run_time, state} = :timer.tc( fn -> set_attributes_to_state(state, fbe_attrs) end ) state = add_statistics_to_state(state, run_time) {: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