use alloc::{vec::Vec, collections::{BTreeMap, VecDeque}, string::String};
use core::cmp::Ordering;
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
use crate::{
Result, SwarmError, AgentId, TaskId, Task, TaskPriority, TaskStatus,
Agent, AgentState, ResourceRequirements,
};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SchedulerConfig {
pub max_concurrent_tasks: usize,
pub task_queue_size: usize,
pub selection_strategy: AgentSelectionStrategy,
pub load_balancing: bool,
pub preemption: bool,
pub dependency_resolution: bool,
}
impl Default for SchedulerConfig {
fn default() -> Self {
Self {
max_concurrent_tasks: 256,
task_queue_size: 10000,
selection_strategy: AgentSelectionStrategy::LoadBalanced,
load_balancing: true,
preemption: false,
dependency_resolution: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum AgentSelectionStrategy {
RoundRobin,
LeastLoaded,
LoadBalanced,
CapabilityBased,
AffinityBased,
}
#[derive(Debug, Clone)]
pub struct AgentWorkload {
pub agent_id: AgentId,
pub capabilities: Vec<String>,
pub current_tasks: usize,
pub max_tasks: usize,
pub total_completed: u64,
pub avg_completion_time: u64,
pub load_factor: f32,
}
impl AgentWorkload {
pub fn new(agent_id: AgentId, capabilities: Vec<String>, max_tasks: usize) -> Self {
Self {
agent_id,
capabilities,
current_tasks: 0,
max_tasks,
total_completed: 0,
avg_completion_time: 0,
load_factor: 0.0,
}
}
pub fn update_load_factor(&mut self) {
self.load_factor = if self.max_tasks > 0 {
self.current_tasks as f32 / self.max_tasks as f32
} else {
1.0
};
}
pub fn can_accept_task(&self) -> bool {
self.current_tasks < self.max_tasks
}
pub fn has_capabilities(&self, required: &[String]) -> bool {
required.iter().all(|cap| self.capabilities.contains(cap))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ExecutionPlan {
pub id: u64,
pub assignments: Vec<TaskAssignment>,
pub dependencies: BTreeMap<TaskId, Vec<TaskId>>,
pub estimated_duration: u64,
pub parallelism_factor: f32,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TaskAssignment {
pub task_id: TaskId,
pub agent_id: AgentId,
pub estimated_start: u64,
pub estimated_completion: u64,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SchedulerStats {
pub total_submitted: u64,
pub total_completed: u64,
pub total_failed: u64,
pub queue_depth: usize,
pub active_tasks: usize,
pub avg_queue_time: f32,
pub avg_execution_time: f32,
pub agent_utilization: BTreeMap<AgentId, f32>,
}
impl Default for SchedulerStats {
fn default() -> Self {
Self {
total_submitted: 0,
total_completed: 0,
total_failed: 0,
queue_depth: 0,
active_tasks: 0,
avg_queue_time: 0.0,
avg_execution_time: 0.0,
agent_utilization: BTreeMap::new(),
}
}
}
pub struct TaskScheduler {
config: SchedulerConfig,
task_queue: Vec<Task>,
running_tasks: BTreeMap<TaskId, TaskExecution>,
completed_tasks: Vec<TaskId>,
agent_workloads: BTreeMap<AgentId, AgentWorkload>,
stats: SchedulerStats,
round_robin_counter: usize,
}
#[derive(Debug, Clone)]
struct TaskExecution {
task_id: TaskId,
agent_id: AgentId,
start_time: u64,
expected_completion: u64,
}
impl TaskScheduler {
pub fn new(config: SchedulerConfig) -> Self {
Self {
config,
task_queue: Vec::new(),
running_tasks: BTreeMap::new(),
completed_tasks: Vec::new(),
agent_workloads: BTreeMap::new(),
stats: SchedulerStats::default(),
round_robin_counter: 0,
}
}
pub fn register_agent(&mut self, agent_id: AgentId, capabilities: Vec<String>, max_tasks: usize) -> Result<()> {
let workload = AgentWorkload::new(agent_id, capabilities, max_tasks);
self.agent_workloads.insert(agent_id, workload);
self.stats.agent_utilization.insert(agent_id, 0.0);
Ok(())
}
pub fn unregister_agent(&mut self, agent_id: AgentId) -> Result<()> {
let cancelled_tasks: Vec<_> = self.running_tasks.iter()
.filter(|(_, execution)| execution.agent_id == agent_id)
.map(|(task_id, _)| *task_id)
.collect();
for task_id in cancelled_tasks {
self.cancel_task(task_id)?;
}
self.agent_workloads.remove(&agent_id);
self.stats.agent_utilization.remove(&agent_id);
Ok(())
}
pub fn submit_task(&mut self, mut task: Task) -> Result<()> {
if self.task_queue.len() >= self.config.task_queue_size {
return Err(SwarmError::scheduler("Task queue is full"));
}
if self.config.dependency_resolution {
self.validate_dependencies(&task)?;
}
let insert_pos = self.task_queue.iter()
.position(|t| t.priority < task.priority)
.unwrap_or(self.task_queue.len());
task.status = TaskStatus::Pending;
self.task_queue.insert(insert_pos, task);
self.stats.total_submitted += 1;
self.stats.queue_depth = self.task_queue.len();
Ok(())
}
pub fn submit_batch(&mut self, tasks: Vec<Task>) -> Result<()> {
for task in tasks {
self.submit_task(task)?;
}
Ok(())
}
pub fn create_execution_plan(&mut self) -> Result<ExecutionPlan> {
static mut PLAN_COUNTER: u64 = 0;
let plan_id = unsafe {
PLAN_COUNTER += 1;
PLAN_COUNTER
};
let mut assignments = Vec::new();
let mut dependencies = BTreeMap::new();
let mut current_time = self.get_current_time();
for task in &self.task_queue {
dependencies.insert(task.id, task.dependencies.clone());
}
for task in &self.task_queue {
if self.is_task_ready(task) {
if let Some(agent_id) = self.select_agent_for_task(task) {
let estimated_duration = self.estimate_task_duration(task);
assignments.push(TaskAssignment {
task_id: task.id,
agent_id,
estimated_start: current_time,
estimated_completion: current_time + estimated_duration,
});
current_time += estimated_duration / 4; }
}
}
let parallelism_factor = if self.task_queue.len() > 0 {
assignments.len() as f32 / self.task_queue.len() as f32
} else {
1.0
};
Ok(ExecutionPlan {
id: plan_id,
assignments,
dependencies,
estimated_duration: current_time - self.get_current_time(),
parallelism_factor,
})
}
pub fn schedule_tasks(&mut self) -> Result<Vec<TaskId>> {
let mut scheduled_tasks = Vec::new();
if self.running_tasks.len() >= self.config.max_concurrent_tasks {
return Ok(scheduled_tasks);
}
let mut i = 0;
while i < self.task_queue.len() {
let task_ready = self.is_task_ready(&self.task_queue[i]);
if !task_ready {
i += 1;
continue;
}
let task_for_selection = self.task_queue[i].clone();
if let Some(agent_id) = self.select_agent_for_task(&task_for_selection) {
let mut task = self.task_queue.remove(i);
task.schedule(agent_id);
let execution = TaskExecution {
task_id: task.id,
agent_id,
start_time: self.get_current_time(),
expected_completion: self.get_current_time() + self.estimate_task_duration(&task),
};
self.running_tasks.insert(task.id, execution);
scheduled_tasks.push(task.id);
if let Some(workload) = self.agent_workloads.get_mut(&agent_id) {
workload.current_tasks += 1;
workload.update_load_factor();
}
self.stats.active_tasks += 1;
self.stats.queue_depth = self.task_queue.len();
if self.running_tasks.len() >= self.config.max_concurrent_tasks {
break;
}
} else {
i += 1;
}
}
Ok(scheduled_tasks)
}
pub fn complete_task(&mut self, task_id: TaskId, success: bool) -> Result<()> {
let execution = self.running_tasks.remove(&task_id)
.ok_or_else(|| SwarmError::scheduler("Task not found in running tasks"))?;
let execution_time = if success {
self.get_current_time() - execution.start_time
} else {
0
};
if let Some(workload) = self.agent_workloads.get_mut(&execution.agent_id) {
workload.current_tasks = workload.current_tasks.saturating_sub(1);
workload.update_load_factor();
if success {
workload.total_completed += 1;
workload.avg_completion_time =
(workload.avg_completion_time + execution_time) / 2;
}
}
if success {
self.stats.total_completed += 1;
self.completed_tasks.push(task_id);
} else {
self.stats.total_failed += 1;
}
self.stats.active_tasks = self.running_tasks.len();
self.update_agent_utilization();
Ok(())
}
pub fn cancel_task(&mut self, task_id: TaskId) -> Result<()> {
if let Some(execution) = self.running_tasks.remove(&task_id) {
if let Some(workload) = self.agent_workloads.get_mut(&execution.agent_id) {
workload.current_tasks = workload.current_tasks.saturating_sub(1);
workload.update_load_factor();
}
self.stats.active_tasks = self.running_tasks.len();
self.update_agent_utilization();
}
Ok(())
}
pub fn stats(&self) -> &SchedulerStats {
&self.stats
}
pub fn get_task_status(&self, task_id: TaskId) -> Option<TaskStatus> {
if self.running_tasks.contains_key(&task_id) {
return Some(TaskStatus::Running);
}
if self.completed_tasks.contains(&task_id) {
return Some(TaskStatus::Completed);
}
for task in &self.task_queue {
if task.id == task_id {
return Some(task.status);
}
}
None
}
pub fn balance_load(&mut self) -> Result<u64> {
if !self.config.load_balancing {
return Ok(0);
}
let mut rebalanced = 0;
let total_load: usize = self.agent_workloads.values()
.map(|w| w.current_tasks)
.sum();
let avg_load = if self.agent_workloads.len() > 0 {
total_load as f32 / self.agent_workloads.len() as f32
} else {
0.0
};
let overloaded_threshold = avg_load * 1.5;
let underloaded_threshold = avg_load * 0.5;
let overloaded: Vec<_> = self.agent_workloads.iter()
.filter(|(_, w)| w.current_tasks as f32 > overloaded_threshold && w.current_tasks > 0)
.map(|(id, _)| *id)
.collect();
let underloaded: Vec<_> = self.agent_workloads.iter()
.filter(|(_, w)| (w.current_tasks as f32) < underloaded_threshold && w.can_accept_task())
.map(|(id, _)| *id)
.collect();
rebalanced = overloaded.len().min(underloaded.len()) as u64;
Ok(rebalanced)
}
fn is_task_ready(&self, task: &Task) -> bool {
if !self.config.dependency_resolution {
return true;
}
task.dependencies.iter().all(|dep_id| {
self.completed_tasks.contains(dep_id)
})
}
fn select_agent_for_task(&mut self, task: &Task) -> Option<AgentId> {
let available_agents: Vec<_> = self.agent_workloads.iter()
.filter(|(_, workload)| {
workload.can_accept_task() &&
workload.has_capabilities(&task.required_capabilities)
})
.collect();
if available_agents.is_empty() {
return None;
}
match self.config.selection_strategy {
AgentSelectionStrategy::RoundRobin => {
let selected = available_agents[self.round_robin_counter % available_agents.len()];
self.round_robin_counter += 1;
Some(*selected.0)
}
AgentSelectionStrategy::LeastLoaded => {
available_agents.iter()
.min_by_key(|(_, workload)| workload.current_tasks)
.map(|(id, _)| **id)
}
AgentSelectionStrategy::LoadBalanced => {
available_agents.iter()
.min_by(|(_, a), (_, b)| {
a.load_factor.partial_cmp(&b.load_factor)
.unwrap_or(Ordering::Equal)
})
.map(|(id, _)| **id)
}
AgentSelectionStrategy::CapabilityBased => {
available_agents.iter()
.max_by_key(|(_, workload)| {
task.required_capabilities.iter()
.filter(|cap| workload.capabilities.contains(cap))
.count()
})
.map(|(id, _)| **id)
}
AgentSelectionStrategy::AffinityBased => {
if let Some(preferred) = task.preferred_agent {
if available_agents.iter().any(|(id, _)| **id == preferred) {
return Some(preferred);
}
}
available_agents.iter()
.min_by_key(|(_, workload)| workload.current_tasks)
.map(|(id, _)| **id)
}
}
}
fn validate_dependencies(&self, task: &Task) -> Result<()> {
for dep_id in &task.dependencies {
if *dep_id == task.id {
return Err(SwarmError::scheduler("Circular dependency detected"));
}
}
Ok(())
}
fn estimate_task_duration(&self, task: &Task) -> u64 {
let base_time = 100; let payload_factor = (task.payload.len() / 1024).max(1) as u64;
let priority_factor = match task.priority {
TaskPriority::Critical => 50,
TaskPriority::High => 75,
TaskPriority::Normal => 100,
TaskPriority::Low => 150,
};
base_time * payload_factor * priority_factor / 100
}
fn get_current_time(&self) -> u64 {
static mut COUNTER: u64 = 0;
unsafe {
COUNTER += 1;
COUNTER
}
}
fn update_agent_utilization(&mut self) {
for (agent_id, workload) in &self.agent_workloads {
self.stats.agent_utilization.insert(*agent_id, workload.load_factor);
}
}
}