use alloc::{vec::Vec, collections::BTreeMap, string::String, boxed::Box, vec};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
use crate::{
Result, SwarmError, AgentId, TaskId, Message, MessageType, MessagePayload,
SwarmTopology, Agent, AgentState, Task, TaskPriority,
};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CoordinationConfig {
pub topology: SwarmTopology,
pub max_group_size: usize,
pub consensus_threshold: f32,
pub fault_tolerance: bool,
pub leader_election: bool,
pub heartbeat_interval: u64,
}
impl Default for CoordinationConfig {
fn default() -> Self {
Self {
topology: SwarmTopology::Mesh,
max_group_size: 16,
consensus_threshold: 0.67, fault_tolerance: true,
leader_election: true,
heartbeat_interval: 100,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CoordinationRole {
Leader,
Follower,
Observer,
Candidate,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ConsensusState {
Proposing,
Voting,
Decided,
Failed,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ConsensusProposal {
pub id: u64,
pub proposer: AgentId,
pub proposal_type: ProposalType,
pub data: Vec<u8>,
pub required_votes: usize,
pub votes: BTreeMap<AgentId, Vote>,
pub state: ConsensusState,
pub created_at: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ProposalType {
TaskAssignment,
ResourceAllocation,
AgentRegistration,
ConfigurationChange,
EmergencyResponse,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Vote {
pub voter: AgentId,
pub decision: VoteDecision,
pub timestamp: u64,
pub reason: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum VoteDecision {
Approve,
Reject,
Abstain,
}
#[derive(Debug, Clone)]
pub struct CoordinationGroup {
pub id: u64,
pub members: Vec<AgentId>,
pub leader: Option<AgentId>,
pub capabilities: Vec<String>,
pub active_proposals: BTreeMap<u64, ConsensusProposal>,
pub health: GroupHealth,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GroupHealth {
Healthy,
Degraded,
Unhealthy,
Failed,
}
pub struct SwarmCoordinator {
config: CoordinationConfig,
groups: BTreeMap<u64, CoordinationGroup>,
agent_roles: BTreeMap<AgentId, CoordinationRole>,
agent_health: BTreeMap<AgentId, AgentHealthInfo>,
global_leader: Option<AgentId>,
global_proposals: BTreeMap<u64, ConsensusProposal>,
stats: CoordinationStats,
}
#[derive(Debug, Clone)]
struct AgentHealthInfo {
agent_id: AgentId,
last_heartbeat: u64,
health: AgentHealth,
failure_count: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AgentHealth {
Healthy,
Degraded,
Unhealthy,
Failed,
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CoordinationStats {
pub total_proposals: u64,
pub successful_consensus: u64,
pub failed_consensus: u64,
pub leader_elections: u64,
pub agent_failures: u64,
pub group_reconfigurations: u64,
}
impl SwarmCoordinator {
pub fn new(config: CoordinationConfig) -> Self {
Self {
config,
groups: BTreeMap::new(),
agent_roles: BTreeMap::new(),
agent_health: BTreeMap::new(),
global_leader: None,
global_proposals: BTreeMap::new(),
stats: CoordinationStats::default(),
}
}
pub fn initialize(&mut self) -> Result<()> {
match self.config.topology {
SwarmTopology::Centralized | SwarmTopology::Star => {
self.create_group(Vec::new())?;
}
SwarmTopology::Mesh | SwarmTopology::Hierarchical | SwarmTopology::Ring => {
}
}
Ok(())
}
pub fn register_agent(&mut self, agent_id: AgentId, capabilities: Vec<String>) -> Result<()> {
self.agent_health.insert(agent_id, AgentHealthInfo {
agent_id,
last_heartbeat: self.get_current_time(),
health: AgentHealth::Healthy,
failure_count: 0,
});
let role = match self.config.topology {
SwarmTopology::Centralized | SwarmTopology::Star => {
if self.global_leader.is_none() {
self.global_leader = Some(agent_id);
CoordinationRole::Leader
} else {
CoordinationRole::Follower
}
}
_ => CoordinationRole::Follower,
};
self.agent_roles.insert(agent_id, role);
self.assign_to_group(agent_id, capabilities)?;
Ok(())
}
pub fn unregister_agent(&mut self, agent_id: AgentId) -> Result<()> {
let mut groups_needing_election = Vec::new();
for group in self.groups.values_mut() {
group.members.retain(|&id| id != agent_id);
if group.leader == Some(agent_id) {
group.leader = None;
if !group.members.is_empty() {
groups_needing_election.push(group.id);
}
}
}
for group_id in groups_needing_election {
self.elect_group_leader(group_id)?;
}
if self.global_leader == Some(agent_id) {
self.global_leader = None;
self.elect_global_leader()?;
}
self.agent_roles.remove(&agent_id);
self.agent_health.remove(&agent_id);
Ok(())
}
pub fn submit_proposal(
&mut self,
proposer: AgentId,
proposal_type: ProposalType,
data: Vec<u8>
) -> Result<u64> {
static mut PROPOSAL_COUNTER: u64 = 0;
let proposal_id = unsafe {
PROPOSAL_COUNTER += 1;
PROPOSAL_COUNTER
};
let total_agents = self.agent_roles.len();
let required_votes = ((total_agents as f32) * self.config.consensus_threshold).ceil() as usize;
let proposal = ConsensusProposal {
id: proposal_id,
proposer,
proposal_type,
data,
required_votes,
votes: BTreeMap::new(),
state: ConsensusState::Proposing,
created_at: self.get_current_time(),
};
self.global_proposals.insert(proposal_id, proposal);
self.stats.total_proposals += 1;
Ok(proposal_id)
}
pub fn cast_vote(
&mut self,
proposal_id: u64,
voter: AgentId,
decision: VoteDecision,
reason: Option<String>
) -> Result<()> {
let timestamp = self.get_current_time();
let proposal = self.global_proposals.get_mut(&proposal_id)
.ok_or_else(|| SwarmError::not_found("Proposal not found"))?;
if proposal.state != ConsensusState::Voting && proposal.state != ConsensusState::Proposing {
return Err(SwarmError::invalid_state("Proposal is not accepting votes"));
}
let vote = Vote {
voter,
decision,
timestamp,
reason,
};
proposal.votes.insert(voter, vote);
proposal.state = ConsensusState::Voting;
self.check_consensus(proposal_id)?;
Ok(())
}
pub fn process_heartbeat(&mut self, agent_id: AgentId) -> Result<()> {
let current_time = self.get_current_time();
if let Some(health_info) = self.agent_health.get_mut(&agent_id) {
health_info.last_heartbeat = current_time;
health_info.health = AgentHealth::Healthy;
health_info.failure_count = 0;
}
Ok(())
}
pub fn check_agent_health(&mut self) -> Result<Vec<AgentId>> {
let current_time = self.get_current_time();
let timeout_threshold = self.config.heartbeat_interval * 3;
let mut failed_agents = Vec::new();
for (agent_id, health_info) in self.agent_health.iter_mut() {
let time_since_heartbeat = current_time - health_info.last_heartbeat;
if time_since_heartbeat > timeout_threshold {
health_info.failure_count += 1;
let new_health = match health_info.failure_count {
1..=2 => AgentHealth::Degraded,
3..=5 => AgentHealth::Unhealthy,
_ => AgentHealth::Failed,
};
if health_info.health != new_health {
health_info.health = new_health;
if new_health == AgentHealth::Failed {
failed_agents.push(*agent_id);
self.stats.agent_failures += 1;
}
}
}
}
for agent_id in &failed_agents {
self.handle_agent_failure(*agent_id)?;
}
Ok(failed_agents)
}
pub fn stats(&self) -> &CoordinationStats {
&self.stats
}
pub fn get_agent_role(&self, agent_id: AgentId) -> Option<CoordinationRole> {
self.agent_roles.get(&agent_id).copied()
}
pub fn get_leader(&self) -> Option<AgentId> {
self.global_leader
}
pub fn get_groups(&self) -> Vec<&CoordinationGroup> {
self.groups.values().collect()
}
fn create_group(&mut self, initial_members: Vec<AgentId>) -> Result<u64> {
static mut GROUP_COUNTER: u64 = 0;
let group_id = unsafe {
GROUP_COUNTER += 1;
GROUP_COUNTER
};
let group = CoordinationGroup {
id: group_id,
members: initial_members,
leader: None,
capabilities: Vec::new(),
active_proposals: BTreeMap::new(),
health: GroupHealth::Healthy,
};
self.groups.insert(group_id, group);
Ok(group_id)
}
fn assign_to_group(&mut self, agent_id: AgentId, capabilities: Vec<String>) -> Result<()> {
match self.config.topology {
SwarmTopology::Centralized | SwarmTopology::Star => {
if let Some(group) = self.groups.values_mut().next() {
if !group.members.contains(&agent_id) {
group.members.push(agent_id);
group.capabilities.extend(capabilities);
group.capabilities.sort();
group.capabilities.dedup();
}
}
}
SwarmTopology::Mesh => {
let suitable_group = self.groups.values_mut()
.find(|group| {
group.members.len() < self.config.max_group_size &&
capabilities.iter().any(|cap| group.capabilities.contains(cap))
});
if let Some(group) = suitable_group {
group.members.push(agent_id);
} else {
let group_id = self.create_group(vec![agent_id])?;
if let Some(group) = self.groups.get_mut(&group_id) {
group.capabilities = capabilities;
}
}
}
SwarmTopology::Hierarchical => {
let group_level = agent_id.raw() % 3; let suitable_group = self.groups.values_mut()
.find(|group| group.id % 3 == group_level && group.members.len() < self.config.max_group_size);
if let Some(group) = suitable_group {
group.members.push(agent_id);
} else {
self.create_group(vec![agent_id])?;
}
}
SwarmTopology::Ring => {
if self.groups.is_empty() {
self.create_group(vec![agent_id])?;
} else if let Some(group) = self.groups.values_mut().next() {
group.members.push(agent_id);
}
}
}
Ok(())
}
fn elect_group_leader(&mut self, group_id: u64) -> Result<()> {
let group = self.groups.get_mut(&group_id)
.ok_or_else(|| SwarmError::not_found("Group not found"))?;
if group.members.is_empty() {
return Ok(());
}
let new_leader = *group.members.iter().min().unwrap();
group.leader = Some(new_leader);
self.agent_roles.insert(new_leader, CoordinationRole::Leader);
for &member in &group.members {
if member != new_leader {
self.agent_roles.insert(member, CoordinationRole::Follower);
}
}
self.stats.leader_elections += 1;
Ok(())
}
fn elect_global_leader(&mut self) -> Result<()> {
if !self.config.leader_election {
return Ok(());
}
let healthy_agents: Vec<_> = self.agent_health.iter()
.filter(|(_, health)| matches!(health.health, AgentHealth::Healthy))
.map(|(id, _)| *id)
.collect();
if healthy_agents.is_empty() {
return Ok(());
}
let new_leader = *healthy_agents.iter().max().unwrap();
self.global_leader = Some(new_leader);
self.agent_roles.insert(new_leader, CoordinationRole::Leader);
self.stats.leader_elections += 1;
Ok(())
}
fn check_consensus(&mut self, proposal_id: u64) -> Result<()> {
let proposal = self.global_proposals.get_mut(&proposal_id)
.ok_or_else(|| SwarmError::not_found("Proposal not found"))?;
let approve_votes = proposal.votes.values()
.filter(|vote| vote.decision == VoteDecision::Approve)
.count();
let reject_votes = proposal.votes.values()
.filter(|vote| vote.decision == VoteDecision::Reject)
.count();
if approve_votes >= proposal.required_votes {
proposal.state = ConsensusState::Decided;
self.stats.successful_consensus += 1;
} else if reject_votes > (proposal.votes.len() - proposal.required_votes) {
proposal.state = ConsensusState::Failed;
self.stats.failed_consensus += 1;
}
Ok(())
}
fn handle_agent_failure(&mut self, agent_id: AgentId) -> Result<()> {
if !self.config.fault_tolerance {
return Ok(());
}
let mut groups_needing_election = Vec::new();
for group in self.groups.values_mut() {
group.members.retain(|&id| id != agent_id);
if group.leader == Some(agent_id) {
group.leader = None;
if !group.members.is_empty() {
groups_needing_election.push(group.id);
}
}
}
for group_id in groups_needing_election {
self.elect_group_leader(group_id)?;
}
if self.global_leader == Some(agent_id) {
self.global_leader = None;
self.elect_global_leader()?;
}
Ok(())
}
fn get_current_time(&self) -> u64 {
static mut COUNTER: u64 = 0;
unsafe {
COUNTER += 1;
COUNTER
}
}
}