pub use peat_schema::node::v1::{
AuthorityLevel, BindingType, HumanMachinePair, Operator, OperatorRank,
};
pub trait OperatorExt {
fn new(
id: String,
name: String,
rank: OperatorRank,
authority: AuthorityLevel,
mos: String,
) -> Self;
fn update_cognitive_load(&mut self, load: f32);
fn update_fatigue(&mut self, fatigue: f32);
fn cognitive_load(&self) -> f32;
fn fatigue(&self) -> f32;
fn is_overloaded(&self, threshold: f32) -> bool;
fn is_fatigued(&self, threshold: f32) -> bool;
fn effectiveness(&self) -> f32;
}
impl OperatorExt for Operator {
fn new(
id: String,
name: String,
rank: OperatorRank,
authority: AuthorityLevel,
mos: String,
) -> Self {
use serde_json::json;
let metadata = json!({
"cognitive_load": 0.0,
"fatigue": 0.0,
});
Self {
id,
name,
rank: rank as i32,
authority_level: authority as i32,
mos,
metadata_json: metadata.to_string(),
}
}
fn update_cognitive_load(&mut self, load: f32) {
let load = load.clamp(0.0, 1.0);
let mut metadata: serde_json::Value =
serde_json::from_str(&self.metadata_json).unwrap_or(serde_json::json!({}));
metadata["cognitive_load"] = serde_json::json!(load);
self.metadata_json = metadata.to_string();
}
fn update_fatigue(&mut self, fatigue: f32) {
let fatigue = fatigue.clamp(0.0, 1.0);
let mut metadata: serde_json::Value =
serde_json::from_str(&self.metadata_json).unwrap_or(serde_json::json!({}));
metadata["fatigue"] = serde_json::json!(fatigue);
self.metadata_json = metadata.to_string();
}
fn cognitive_load(&self) -> f32 {
let metadata: serde_json::Value =
serde_json::from_str(&self.metadata_json).unwrap_or(serde_json::json!({}));
metadata["cognitive_load"].as_f64().unwrap_or(0.0) as f32
}
fn fatigue(&self) -> f32 {
let metadata: serde_json::Value =
serde_json::from_str(&self.metadata_json).unwrap_or(serde_json::json!({}));
metadata["fatigue"].as_f64().unwrap_or(0.0) as f32
}
fn is_overloaded(&self, threshold: f32) -> bool {
self.cognitive_load() > threshold
}
fn is_fatigued(&self, threshold: f32) -> bool {
self.fatigue() > threshold
}
fn effectiveness(&self) -> f32 {
let cognitive_factor = 1.0 - self.cognitive_load();
let fatigue_factor = 1.0 - self.fatigue();
(cognitive_factor * 0.6 + fatigue_factor * 0.4).clamp(0.0, 1.0)
}
}
pub trait OperatorRankExt {
fn to_score(self) -> f64;
fn name(self) -> &'static str;
}
impl OperatorRankExt for OperatorRank {
fn to_score(self) -> f64 {
match self {
Self::Unspecified => 0.0,
Self::E1 => 0.10,
Self::E2 => 0.15,
Self::E3 => 0.20,
Self::E4 => 0.30,
Self::E5 => 0.40,
Self::E6 => 0.50,
Self::E7 => 0.60, Self::E8 => 0.70,
Self::E9 => 0.80,
Self::W1 => 0.70,
Self::W2 => 0.75,
Self::W3 => 0.80,
Self::W4 => 0.85,
Self::W5 => 0.90,
Self::O1 => 0.85,
Self::O2 => 0.90,
Self::O3 => 0.95, Self::O4 => 0.97,
Self::O5 => 0.98,
Self::O6 => 0.99,
Self::O7 => 0.995,
Self::O8 => 0.997,
Self::O9 => 0.999,
Self::O10 => 1.0,
}
}
fn name(self) -> &'static str {
match self {
Self::Unspecified => "Unspecified",
Self::E1 => "Private (E-1)",
Self::E2 => "Private (E-2)",
Self::E3 => "Private First Class",
Self::E4 => "Corporal/Specialist",
Self::E5 => "Sergeant",
Self::E6 => "Staff Sergeant",
Self::E7 => "Sergeant First Class",
Self::E8 => "Master Sergeant",
Self::E9 => "Sergeant Major",
Self::W1 => "Warrant Officer 1",
Self::W2 => "Chief Warrant Officer 2",
Self::W3 => "Chief Warrant Officer 3",
Self::W4 => "Chief Warrant Officer 4",
Self::W5 => "Chief Warrant Officer 5",
Self::O1 => "Second Lieutenant",
Self::O2 => "First Lieutenant",
Self::O3 => "Captain",
Self::O4 => "Major",
Self::O5 => "Lieutenant Colonel",
Self::O6 => "Colonel",
Self::O7 => "Brigadier General",
Self::O8 => "Major General",
Self::O9 => "Lieutenant General",
Self::O10 => "General",
}
}
}
pub trait AuthorityLevelExt {
fn to_score(self) -> f64;
fn can_override(self) -> bool;
fn requires_approval(self) -> bool;
}
impl AuthorityLevelExt for AuthorityLevel {
fn to_score(self) -> f64 {
match self {
Self::Unspecified => 0.0,
Self::Observer => 0.1,
Self::Advisor => 0.3,
Self::Supervisor => 0.5,
Self::Commander => 0.8,
}
}
fn can_override(self) -> bool {
matches!(self, Self::Supervisor | Self::Commander)
}
fn requires_approval(self) -> bool {
matches!(self, Self::Commander)
}
}
pub trait HumanMachinePairExt {
fn new(operators: Vec<Operator>, platform_ids: Vec<String>, binding_type: BindingType) -> Self;
fn autonomous(platform_id: String) -> Self;
fn one_to_one(operator: Operator, platform_id: String) -> Self;
fn is_autonomous(&self) -> bool;
fn primary_operator(&self) -> Option<&Operator>;
fn max_rank(&self) -> Option<OperatorRank>;
fn max_authority(&self) -> Option<AuthorityLevel>;
fn has_overloaded_operator(&self, threshold: f32) -> bool;
fn avg_effectiveness(&self) -> f32;
}
impl HumanMachinePairExt for HumanMachinePair {
fn new(operators: Vec<Operator>, platform_ids: Vec<String>, binding_type: BindingType) -> Self {
Self {
operators,
platform_ids,
binding_type: binding_type as i32,
bound_at: None,
}
}
fn autonomous(platform_id: String) -> Self {
Self {
operators: Vec::new(),
platform_ids: vec![platform_id],
binding_type: BindingType::Unspecified as i32,
bound_at: None,
}
}
fn one_to_one(operator: Operator, platform_id: String) -> Self {
Self::new(vec![operator], vec![platform_id], BindingType::OneToOne)
}
fn is_autonomous(&self) -> bool {
self.operators.is_empty()
}
fn primary_operator(&self) -> Option<&Operator> {
self.operators.iter().max_by(|a, b| a.rank.cmp(&b.rank))
}
fn max_rank(&self) -> Option<OperatorRank> {
self.operators
.iter()
.map(|op| OperatorRank::try_from(op.rank).ok())
.max()
.flatten()
}
fn max_authority(&self) -> Option<AuthorityLevel> {
self.operators
.iter()
.map(|op| AuthorityLevel::try_from(op.authority_level).ok())
.max()
.flatten()
}
fn has_overloaded_operator(&self, threshold: f32) -> bool {
self.operators.iter().any(|op| op.is_overloaded(threshold))
}
fn avg_effectiveness(&self) -> f32 {
if self.operators.is_empty() {
return 1.0; }
let sum: f32 = self.operators.iter().map(|op| op.effectiveness()).sum();
sum / self.operators.len() as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operator_creation() {
let op = Operator::new(
"op1".to_string(),
"John Doe".to_string(),
OperatorRank::E7,
AuthorityLevel::Commander,
"11B".to_string(),
);
assert_eq!(op.id, "op1");
assert_eq!(op.rank, OperatorRank::E7 as i32);
assert_eq!(op.cognitive_load(), 0.0);
assert_eq!(op.fatigue(), 0.0);
}
#[test]
fn test_operator_cognitive_load() {
let mut op = Operator::new(
"op1".to_string(),
"John".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op.update_cognitive_load(0.8);
assert_eq!(op.cognitive_load(), 0.8);
assert!(op.is_overloaded(0.7));
assert!(!op.is_overloaded(0.9));
op.update_cognitive_load(1.5);
assert_eq!(op.cognitive_load(), 1.0);
}
#[test]
fn test_operator_effectiveness() {
let mut op = Operator::new(
"op1".to_string(),
"John".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
assert_eq!(op.effectiveness(), 1.0);
op.update_cognitive_load(0.8);
op.update_fatigue(0.2);
let eff = op.effectiveness();
assert!(eff > 0.0 && eff < 1.0);
op.update_cognitive_load(0.9);
op.update_fatigue(0.9);
assert!(op.effectiveness() < 0.2);
}
#[test]
fn test_rank_ordering() {
assert!(OperatorRank::E7 > OperatorRank::E5);
assert!(OperatorRank::O3 > OperatorRank::E9);
assert!(OperatorRank::W5 > OperatorRank::W1);
}
#[test]
fn test_rank_to_score() {
assert_eq!(OperatorRank::E1.to_score(), 0.10);
assert_eq!(OperatorRank::E7.to_score(), 0.60);
assert_eq!(OperatorRank::O3.to_score(), 0.95);
assert_eq!(OperatorRank::O10.to_score(), 1.0);
}
#[test]
fn test_authority_level_ordering() {
assert!(AuthorityLevel::Commander > AuthorityLevel::Supervisor);
assert!(AuthorityLevel::Commander > AuthorityLevel::Observer);
}
#[test]
fn test_authority_can_override() {
assert!(!AuthorityLevel::Observer.can_override());
assert!(!AuthorityLevel::Advisor.can_override());
assert!(AuthorityLevel::Supervisor.can_override());
assert!(AuthorityLevel::Commander.can_override());
}
#[test]
fn test_human_machine_pair_autonomous() {
let pair = HumanMachinePair::autonomous("node_1".to_string());
assert!(pair.is_autonomous());
assert_eq!(pair.operators.len(), 0);
assert_eq!(pair.avg_effectiveness(), 1.0);
}
#[test]
fn test_human_machine_pair_one_to_one() {
let op = Operator::new(
"op1".to_string(),
"John".to_string(),
OperatorRank::E5,
AuthorityLevel::Commander,
"11B".to_string(),
);
let pair = HumanMachinePair::one_to_one(op, "node_1".to_string());
assert!(!pair.is_autonomous());
assert_eq!(pair.operators.len(), 1);
assert_eq!(pair.binding_type, BindingType::OneToOne as i32);
assert_eq!(pair.max_rank(), Some(OperatorRank::E5));
}
#[test]
fn test_human_machine_pair_primary_operator() {
let op1 = Operator::new(
"op1".to_string(),
"John".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
let op2 = Operator::new(
"op2".to_string(),
"Jane".to_string(),
OperatorRank::E7,
AuthorityLevel::Commander,
"11B".to_string(),
);
let pair = HumanMachinePair::new(
vec![op1, op2],
vec!["node_1".to_string()],
BindingType::ManyToOne,
);
let primary = pair.primary_operator().unwrap();
assert_eq!(primary.rank, OperatorRank::E7 as i32);
assert_eq!(primary.name, "Jane");
}
#[test]
fn test_human_machine_pair_max_authority() {
let op1 = Operator::new(
"op1".to_string(),
"John".to_string(),
OperatorRank::E7,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
let op2 = Operator::new(
"op2".to_string(),
"Jane".to_string(),
OperatorRank::E5,
AuthorityLevel::Commander,
"11B".to_string(),
);
let pair = HumanMachinePair::new(
vec![op1, op2],
vec!["node_1".to_string()],
BindingType::ManyToOne,
);
assert_eq!(pair.max_authority(), Some(AuthorityLevel::Commander));
}
#[test]
fn test_human_machine_pair_overloaded_check() {
let mut op1 = Operator::new(
"op1".to_string(),
"John".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op1.update_cognitive_load(0.9);
let op2 = Operator::new(
"op2".to_string(),
"Jane".to_string(),
OperatorRank::E7,
AuthorityLevel::Commander,
"11B".to_string(),
);
let pair = HumanMachinePair::new(
vec![op1, op2],
vec!["node_1".to_string()],
BindingType::ManyToOne,
);
assert!(pair.has_overloaded_operator(0.8));
assert!(!pair.has_overloaded_operator(0.95));
}
#[test]
fn test_operator_cognitive_load_clamping() {
let mut op = Operator::new(
"op1".to_string(),
"Test".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op.update_cognitive_load(2.0);
assert_eq!(op.cognitive_load(), 1.0);
op.update_cognitive_load(-0.5);
assert_eq!(op.cognitive_load(), 0.0);
op.update_cognitive_load(0.5);
assert_eq!(op.cognitive_load(), 0.5);
}
#[test]
fn test_operator_fatigue_clamping() {
let mut op = Operator::new(
"op1".to_string(),
"Test".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op.update_fatigue(1.5);
assert_eq!(op.fatigue(), 1.0);
op.update_fatigue(-0.3);
assert_eq!(op.fatigue(), 0.0);
}
#[test]
fn test_operator_is_overloaded_edge_cases() {
let mut op = Operator::new(
"op1".to_string(),
"Test".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op.update_cognitive_load(0.75);
assert!(!op.is_overloaded(0.75));
assert!(!op.is_overloaded(0.76));
assert!(op.is_overloaded(0.74));
assert!(op.is_overloaded(0.0));
}
#[test]
fn test_operator_is_fatigued_edge_cases() {
let mut op = Operator::new(
"op1".to_string(),
"Test".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op.update_fatigue(0.6);
assert!(!op.is_fatigued(0.6));
assert!(!op.is_fatigued(0.7));
assert!(op.is_fatigued(0.5));
}
#[test]
fn test_operator_effectiveness_edge_cases() {
let mut op = Operator::new(
"op1".to_string(),
"Test".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
assert_eq!(op.effectiveness(), 1.0);
op.update_cognitive_load(1.0);
op.update_fatigue(1.0);
assert_eq!(op.effectiveness(), 0.0);
op.update_cognitive_load(1.0);
op.update_fatigue(0.0);
let eff = op.effectiveness();
assert!(eff > 0.0 && eff < 1.0);
assert_eq!(eff, 0.4); }
#[test]
fn test_operator_metadata_json_invalid() {
let mut op = Operator::new(
"op1".to_string(),
"Test".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op.metadata_json = "not valid json".to_string();
assert_eq!(op.cognitive_load(), 0.0);
assert_eq!(op.fatigue(), 0.0);
}
#[test]
fn test_rank_to_score_all_ranks() {
let ranks = vec![
(OperatorRank::E1, 0.10),
(OperatorRank::E2, 0.15),
(OperatorRank::E3, 0.20),
(OperatorRank::E4, 0.30),
(OperatorRank::E5, 0.40),
(OperatorRank::E6, 0.50),
(OperatorRank::E7, 0.60),
(OperatorRank::E8, 0.70),
(OperatorRank::E9, 0.80),
(OperatorRank::W1, 0.70),
(OperatorRank::W2, 0.75),
(OperatorRank::W3, 0.80),
(OperatorRank::W4, 0.85),
(OperatorRank::W5, 0.90),
(OperatorRank::O1, 0.85),
(OperatorRank::O2, 0.90),
(OperatorRank::O3, 0.95),
(OperatorRank::O4, 0.97),
(OperatorRank::O5, 0.98),
(OperatorRank::O6, 0.99),
(OperatorRank::O7, 0.995),
(OperatorRank::O8, 0.997),
(OperatorRank::O9, 0.999),
(OperatorRank::O10, 1.0),
];
for (rank, expected_score) in ranks {
let score = rank.to_score();
assert_eq!(
score, expected_score,
"Rank {:?} should have score {}",
rank, expected_score
);
assert!((0.0..=1.0).contains(&score));
}
assert!(OperatorRank::E2.to_score() > OperatorRank::E1.to_score());
assert!(OperatorRank::E9.to_score() > OperatorRank::E8.to_score());
assert!(OperatorRank::W2.to_score() > OperatorRank::W1.to_score());
assert!(OperatorRank::W5.to_score() > OperatorRank::W4.to_score());
assert!(OperatorRank::O2.to_score() > OperatorRank::O1.to_score());
assert!(OperatorRank::O10.to_score() > OperatorRank::O9.to_score());
}
#[test]
fn test_rank_name_all_ranks() {
let ranks = vec![
OperatorRank::Unspecified,
OperatorRank::E1,
OperatorRank::E5,
OperatorRank::E9,
OperatorRank::W1,
OperatorRank::W5,
OperatorRank::O1,
OperatorRank::O10,
];
for rank in ranks {
let name = rank.name();
assert!(!name.is_empty());
}
}
#[test]
fn test_authority_level_to_score() {
assert_eq!(AuthorityLevel::Unspecified.to_score(), 0.0);
assert_eq!(AuthorityLevel::Observer.to_score(), 0.1);
assert_eq!(AuthorityLevel::Advisor.to_score(), 0.3);
assert_eq!(AuthorityLevel::Supervisor.to_score(), 0.5);
assert_eq!(AuthorityLevel::Commander.to_score(), 0.8);
assert!(AuthorityLevel::Commander.to_score() > AuthorityLevel::Supervisor.to_score());
assert!(AuthorityLevel::Supervisor.to_score() > AuthorityLevel::Advisor.to_score());
assert!(AuthorityLevel::Advisor.to_score() > AuthorityLevel::Observer.to_score());
}
#[test]
fn test_authority_requires_approval() {
assert!(!AuthorityLevel::Unspecified.requires_approval());
assert!(!AuthorityLevel::Observer.requires_approval());
assert!(!AuthorityLevel::Advisor.requires_approval());
assert!(!AuthorityLevel::Supervisor.requires_approval());
assert!(AuthorityLevel::Commander.requires_approval());
}
#[test]
fn test_human_machine_pair_avg_effectiveness_multiple() {
let mut op1 = Operator::new(
"op1".to_string(),
"Op1".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op1.update_cognitive_load(0.2);
op1.update_fatigue(0.2);
let mut op2 = Operator::new(
"op2".to_string(),
"Op2".to_string(),
OperatorRank::E6,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
op2.update_cognitive_load(0.8);
op2.update_fatigue(0.8);
let pair = HumanMachinePair::new(
vec![op1.clone(), op2.clone()],
vec!["node_1".to_string()],
BindingType::ManyToOne,
);
let avg = pair.avg_effectiveness();
let expected = (op1.effectiveness() + op2.effectiveness()) / 2.0;
assert_eq!(avg, expected);
}
#[test]
fn test_human_machine_pair_multiple_platforms() {
let op = Operator::new(
"op1".to_string(),
"Operator".to_string(),
OperatorRank::E6,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
let platform_ids = vec![
"p1".to_string(),
"p2".to_string(),
"p3".to_string(),
"p4".to_string(),
"p5".to_string(),
];
let pair = HumanMachinePair::new(vec![op], platform_ids.clone(), BindingType::OneToMany);
assert_eq!(pair.platform_ids.len(), 5);
assert_eq!(pair.operators.len(), 1);
assert!(!pair.is_autonomous());
}
#[test]
fn test_human_machine_pair_max_rank_and_authority_mismatch() {
let op1 = Operator::new(
"op1".to_string(),
"Junior Commander".to_string(),
OperatorRank::E4,
AuthorityLevel::Commander,
"11B".to_string(),
);
let op2 = Operator::new(
"op2".to_string(),
"Senior Advisor".to_string(),
OperatorRank::E8,
AuthorityLevel::Advisor,
"11B".to_string(),
);
let pair = HumanMachinePair::new(
vec![op1, op2],
vec!["node_1".to_string()],
BindingType::ManyToOne,
);
let primary = pair.primary_operator().unwrap();
assert_eq!(primary.rank, OperatorRank::E8 as i32);
assert_eq!(pair.max_authority(), Some(AuthorityLevel::Commander));
assert_eq!(pair.max_rank(), Some(OperatorRank::E8));
}
#[test]
fn test_human_machine_pair_binding_types() {
let op = Operator::new(
"op1".to_string(),
"Operator".to_string(),
OperatorRank::E5,
AuthorityLevel::Supervisor,
"11B".to_string(),
);
for binding_type in [
BindingType::Unspecified,
BindingType::OneToOne,
BindingType::OneToMany,
BindingType::ManyToOne,
BindingType::ManyToMany,
] {
let pair =
HumanMachinePair::new(vec![op.clone()], vec!["node_1".to_string()], binding_type);
assert_eq!(pair.binding_type, binding_type as i32);
}
}
}