defmodule Argos.Parallel.WorkerState do @moduledoc """ Estructura de datos que representa el estado de un worker paralelo. Contiene identificador, estado, progreso, tiempos, resultados y errores. """ @type status :: :started | :running | :finished | :error | :failed_to_start @type result :: %{task_index: integer(), result: any(), timestamp: DateTime.t()} @type t :: %__MODULE__{ id: any(), status: status(), progress: number() | nil, total: integer() | nil, current_task: integer() | nil, started_at: DateTime.t() | nil, finished_at: DateTime.t() | nil, results: [result()], error: any() | nil, error_task: integer() | nil, last_update: DateTime.t() | nil } defstruct [ :id, :status, :progress, :total, :current_task, :started_at, :finished_at, :error, :error_task, :last_update, results: [] ] @doc """ Crea un nuevo WorkerState con estado :started """ def new(id) do %__MODULE__{ id: id, status: :started, started_at: DateTime.utc_now(), last_update: DateTime.utc_now() } end @doc """ Actualiza el estado a :running con información de progreso """ def running(%__MODULE__{} = state, progress, total, task_index) do %__MODULE__{ state | status: :running, progress: progress, total: total, current_task: task_index, last_update: DateTime.utc_now() } end @doc """ Agrega un resultado al worker state """ def add_result(%__MODULE__{} = state, task_index, result) do result_entry = %{ task_index: task_index, result: result, timestamp: DateTime.utc_now() } updated_results = [result_entry | state.results] %__MODULE__{state | results: updated_results, last_update: DateTime.utc_now()} end @doc """ Marca el worker como finished """ def finished(%__MODULE__{} = state) do %__MODULE__{ state | status: :finished, progress: 100, finished_at: DateTime.utc_now(), last_update: DateTime.utc_now() } end @doc """ Marca el worker como error """ def error(%__MODULE__{} = state, reason, task_index) do %__MODULE__{ state | status: :error, error: reason, error_task: task_index, finished_at: DateTime.utc_now(), last_update: DateTime.utc_now() } end @doc """ Actualiza el progreso sin cambiar el estado """ def update_progress(%__MODULE__{} = state, progress, task_index) do %__MODULE__{ state | progress: progress, current_task: task_index, last_update: DateTime.utc_now() } end @doc """ Verifica si el worker ha terminado (exitosa o erroneamente) """ def finished?(%__MODULE__{status: status}) do status in [:finished, :error, :failed_to_start] end @doc """ Obtiene el tiempo transcurrido desde que empezó el worker """ def elapsed_time(%__MODULE__{started_at: nil}), do: 0 def elapsed_time(%__MODULE__{started_at: started_at, finished_at: nil}) do DateTime.diff(DateTime.utc_now(), started_at, :millisecond) end def elapsed_time(%__MODULE__{started_at: started_at, finished_at: finished_at}) do DateTime.diff(finished_at, started_at, :millisecond) end @doc """ Obtiene el porcentaje de progreso basado en tareas completadas """ def calculate_progress(%__MODULE__{total: total, current_task: current_task}) when is_integer(total) and total > 0 and is_integer(current_task) do (current_task / total * 100) |> Float.round(1) end def calculate_progress(_), do: 0 end