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