defmodule TaskManager do
@moduledoc """
Task manager server that can distribute tasks to servers.
# TODO1: Need to make it so that the import is the LIST format, funtion to distribute
"""
use GenServer
def start_link(collision_data) do
GenServer.start_link(__MODULE__, collision_data, name: {:global, :task_manager})
end
def request_task(pid) do
GenServer.cast({:global, :task_manager},{:request_task, pid})
end
def recieve_result(task_id, result) do
GenServer.cast({:global, :task_manager},{:recieve_result, task_id, result})
end
@doc """
Get result from server
!TODO Not good idea this one, rewrite
"""
@spec get_result() :: None
def get_result do
GenServer.call({:global, :task_manager}, {:get_result})
end
@doc """
Add more tasks to TaskManager.
## Parameters
- collision_data: new calculation tasks that will be added to the task manager
## Examples
TaskManager.add_task(tasks)
"""
@spec add_task(List) :: None
def add_task(collision_data) do
GenServer.cast({:global, :task_manager},{:add_task, collision_data})
end
@doc """
Remove all tasks to TaskManager.
## Examples
TaskManager.remove_all_tasks()
"""
@spec remove_all_tasks() :: None
def remove_all_tasks() do
GenServer.cast({:global, :task_manager},{:remove_all_tasks})
end
@impl true
def init(collision_data) do
{:ok, {collision_data, Map.new(), MapSet.new(),Map.new()}}
end
@impl true
def handle_cast({:add_task, new_collision_data}, {collision_data, pending, done_tasks, results}) do
{:noreply, {List.flatten([new_collision_data | collision_data]), pending, done_tasks, results}}
end
@impl true
def handle_cast({:remove_all_tasks}, {collision_data, pending, done_tasks, results}) do
{:noreply, {[], pending, done_tasks, Map.new()}}
end
@impl true
def handle_cast({:request_task, pid}, {collision_data, pending, done_tasks, results}) do
{task , new_collision_data} = cond do
Enum.empty?(collision_data) ->
{nil, []}
true ->
[task | new_collision_data] = collision_data
{task , new_collision_data}
end
new_pending = send_result(pid, pending, task)
{:noreply, {new_collision_data, new_pending, done_tasks, results}}
end
@impl true
def handle_cast({:recieve_result, task_id, result}, {collision_data, pending, done_tasks, results}) do
cond do
MapSet.member?(done_tasks, task_id) ->
{:noreply, {collision_data, pending, done_tasks, results}}
true ->
new_done_tasks = MapSet.put(done_tasks, task_id)
new_pending = Map.delete(pending, task_id)
new_results = Map.put(results, task_id, result)
{:noreply, {collision_data, new_pending, new_done_tasks, new_results}}
end
end
@impl true
def handle_call({:get_result}, _from, {collision_data, pending, done_tasks, results}) do
{:reply, results, {collision_data, pending, done_tasks, results}}
end
defp send_result(pid, pending, task) when is_nil(task) do
if Enum.empty?(pending) do
pending # Nothing to do
else
#TODO: this empty LIST need to be a argument impot!!!!!
#empty_task = %{point1: [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0], indice1: [0,1,2], translate1: [0.0,0.0,0.0], rotate1: [0.0,0.0,0.0,0.0], point2: [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0], indice2: [0,1,2], translate2: [0.0,0.0,0.0], rotate2: [0.0,0.0,0.0,0.0], margin: 0.1}
{task_id, prev_task} ={:empty_task, nil}
Collision.Detector.Server.send_result(pid, task_id, prev_task)
Map.put(Map.delete(pending, task_id), task_id, prev_task)
end
end
defp send_result(pid, pending, task) do
task_id = make_ref()
Collision.Detector.Server.send_result(pid, task_id, task)
Map.put(pending, task_id, task)
end
end
defmodule TaskManagerSupervisor do
use Supervisor
def start_link(collision_data) do
Supervisor.start_link(__MODULE__, collision_data)
end
def init(collision_data) do
workers = [worker(TaskManager, [collision_data])]
supervise(workers, strategy: :one_for_one)
end
end