Packages
elevator_project
0.1.0
Implementation of concurrent message passing system for parallell elevators.
Current section
Files
Jump to
Current section
Files
lib/network.ex
defmodule Network.Listen do
@moduledoc """
Listens for messages from other nodes, and attempts to
connect to the cluster upon reception.
"""
use Task
@max_connect_attempts 10
@doc false
def start_link(recv_port) do
Task.start_link(__MODULE__, :init, [recv_port])
end
@doc """
`init/1` opens a socket and starts listening by calling `listen/1`.
## Parameters
- recv_port: Port number :: integer()
## Return
- no_return
"""
def init(recv_port) do
{:ok, socket} =
:gen_udp.open(recv_port, [:binary, active: false, broadcast: true, reuseaddr: true])
IO.puts("Started listening on port #{recv_port}")
listen(socket)
end
defp listen(socket) do
{:ok, {_ip, _port, node_name}} = :gen_udp.recv(socket, 0)
if node_name not in all_nodes() and node_name != "nonode@nohost" do
IO.puts("Attempting to connect to #{node_name}")
connect_to(node_name)
end
listen(socket)
end
defp connect_to(node_name, attempt \\ 0)
defp connect_to(node_name, attempt) when attempt < @max_connect_attempts do
case Node.ping(String.to_atom(node_name)) do
:pang ->
connect_to(node_name, attempt + 1)
:pong ->
IO.puts("Connected to node #{node_name}")
end
end
defp connect_to(node_name, attempt) when attempt >= @max_connect_attempts do
IO.puts("Gave up connecting to #{node_name}")
end
defp all_nodes() do
Enum.map([Node.self() | Node.list()], fn node_name -> to_string(node_name) end)
end
end
defmodule Network.Broadcast do
@moduledoc """
Broadcasts own node name, so that
the node can be discovered by other nodes.
"""
use Task
@broadcast_sleep_duration 500
@send_port 0
@doc false
def start_link(recv_port) do
Task.start_link(__MODULE__, :init, [recv_port])
end
@doc """
`init/1` initializes `broadcast/2` with a socket and the port
`Network.Listen` is listening on.
## Parameters
- recv_port: Port number `Network.Listen` is listening on :: integer()
## Return
- no_return
"""
def init(recv_port) do
{:ok, socket} =
:gen_udp.open(@send_port, [:binary, active: false, broadcast: true, reuseaddr: true])
IO.puts("Started broadcasting to port #{recv_port}")
broadcast(socket, recv_port)
end
defp broadcast(socket, recv_port) do
:gen_udp.send(socket, {255, 255, 255, 255}, recv_port, to_string(Node.self()))
Process.sleep(@broadcast_sleep_duration)
broadcast(socket, recv_port)
end
end
defmodule Network.ConnectionCheck do
@moduledoc """
Evaluates if the elevator must request a backup, by checking if the node
newly connected to the cluster.
Uses the modules:
- OrderDistributor
"""
use Task
@check_sleep_duration 100
@doc false
def start_link(_init_arg) do
Task.start_link(__MODULE__, :check_connection, [[]])
end
@doc """
Checks the current list of connected nodes and compares it to the previous list of nodes. If
the list goes from empty to not empty, the node requests a backup by calling
´OrderDistributor.request_backup/0´.
## Parameters
- prev_connected_nodes: list of the previously connected nodes :: list()
## Return
- no_return
"""
def check_connection(prev_connected_nodes) do
current_connected_nodes = Node.list()
if Enum.empty?(prev_connected_nodes) and not Enum.empty?(current_connected_nodes) do
OrderDistributor.request_backup()
end
Process.sleep(@check_sleep_duration)
check_connection(current_connected_nodes)
end
end