phytrace_sdk/models/domains/
coordination.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use validator::Validate;
6
7use super::common::Position2D;
8use crate::models::enums::{FleetRole, FormationType, TrafficPriority};
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct CoordinationDomain {
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub fleet_role: Option<FleetRole>,
16
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub formation: Option<Formation>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub neighbors: Option<Vec<NeighborRobot>>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub swarm: Option<SwarmInfo>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub traffic: Option<TrafficManagement>,
32}
33
34#[derive(Debug, Clone, Default, Serialize, Deserialize)]
36pub struct Formation {
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub formation_id: Option<String>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub formation_type: Option<FormationType>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub position_index: Option<u32>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub formation_size: Option<u32>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub position_error_m: Option<f64>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub leader_id: Option<String>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub is_in_formation: Option<bool>,
64}
65
66#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
68pub struct NeighborRobot {
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub robot_id: Option<String>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 #[validate(range(min = 0.0))]
76 pub distance_m: Option<f64>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub bearing_deg: Option<f64>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub velocity_mps: Option<f64>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub heading_deg: Option<f64>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub last_comm: Option<DateTime<Utc>>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub comm_latency_ms: Option<f64>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub collision_risk: Option<bool>,
101}
102
103#[derive(Debug, Clone, Default, Serialize, Deserialize)]
105pub struct SwarmInfo {
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub swarm_id: Option<String>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub swarm_size: Option<u32>,
113
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub state: Option<String>,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub consensus_value: Option<f64>,
121
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub centroid: Option<Position2D>,
125}
126
127#[derive(Debug, Clone, Default, Serialize, Deserialize)]
129pub struct TrafficManagement {
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub priority: Option<TrafficPriority>,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub at_intersection: Option<bool>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub intersection_id: Option<String>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub has_right_of_way: Option<bool>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub waiting_for: Option<Vec<String>>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub wait_time_sec: Option<f64>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub reserved_segments: Option<Vec<String>>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub deadlock_detected: Option<bool>,
161}
162
163impl CoordinationDomain {
164 pub fn new(role: FleetRole) -> Self {
166 Self {
167 fleet_role: Some(role),
168 ..Default::default()
169 }
170 }
171
172 pub fn with_neighbor(mut self, neighbor: NeighborRobot) -> Self {
174 let neighbors = self.neighbors.get_or_insert_with(Vec::new);
175 neighbors.push(neighbor);
176 self
177 }
178
179 pub fn with_formation(mut self, formation: Formation) -> Self {
181 self.formation = Some(formation);
182 self
183 }
184
185 pub fn with_traffic(mut self, traffic: TrafficManagement) -> Self {
187 self.traffic = Some(traffic);
188 self
189 }
190}
191
192impl NeighborRobot {
193 pub fn new(robot_id: impl Into<String>, distance_m: f64) -> Self {
195 Self {
196 robot_id: Some(robot_id.into()),
197 distance_m: Some(distance_m),
198 last_comm: Some(Utc::now()),
199 ..Default::default()
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_coordination_domain() {
210 let coord = CoordinationDomain::new(FleetRole::Follower)
211 .with_neighbor(NeighborRobot::new("robot-002", 3.5));
212
213 assert_eq!(coord.fleet_role, Some(FleetRole::Follower));
214 assert_eq!(coord.neighbors.unwrap().len(), 1);
215 }
216}