phytrace_sdk/models/domains/
environment_interaction.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use validator::Validate;
6
7use super::common::Position2D;
8use crate::models::enums::{ChargingStationState, DoorState, ElevatorState};
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct EnvironmentInteractionDomain {
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub doors: Option<Vec<DoorInteraction>>,
16
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub elevators: Option<Vec<ElevatorInteraction>>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub charging_stations: Option<Vec<ChargingStationInteraction>>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub surface: Option<SurfaceInfo>,
28}
29
30#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
32pub struct DoorInteraction {
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub door_id: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub name: Option<String>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub state: Option<DoorState>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub position: Option<Position2D>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 #[validate(range(min = 0.0))]
52 pub distance_m: Option<f64>,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub is_waiting: Option<bool>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub open_requested: Option<bool>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub wait_time_sec: Option<f64>,
65
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub last_change: Option<DateTime<Utc>>,
69}
70
71#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
73pub struct ElevatorInteraction {
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub elevator_id: Option<String>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub name: Option<String>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub state: Option<ElevatorState>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub current_floor: Option<i32>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub target_floor: Option<i32>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub robot_floor: Option<i32>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub robot_inside: Option<bool>,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub is_reserved: Option<bool>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub eta_sec: Option<f64>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub wait_time_sec: Option<f64>,
113}
114
115#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
117pub struct ChargingStationInteraction {
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub station_id: Option<String>,
121
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub name: Option<String>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub state: Option<ChargingStationState>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub position: Option<Position2D>,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 #[validate(range(min = 0.0))]
137 pub distance_m: Option<f64>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub is_docked: Option<bool>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub is_approaching: Option<bool>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 #[validate(range(min = 0.0, max = 1.0))]
150 pub alignment_score: Option<f64>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub charger_type: Option<String>,
155
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub max_power_w: Option<f64>,
159}
160
161#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
163pub struct SurfaceInfo {
164 #[serde(skip_serializing_if = "Option::is_none")]
166 pub surface_type: Option<String>,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
170 #[validate(range(min = 0.0, max = 1.0))]
171 pub friction: Option<f64>,
172
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub incline_deg: Option<f64>,
176
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub is_wet: Option<bool>,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub is_slippery: Option<bool>,
184
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub roughness: Option<f64>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
191 #[validate(range(min = 0.0, max = 1.0))]
192 pub confidence: Option<f64>,
193}
194
195impl EnvironmentInteractionDomain {
196 pub fn with_door(mut self, door: DoorInteraction) -> Self {
198 let doors = self.doors.get_or_insert_with(Vec::new);
199 doors.push(door);
200 self
201 }
202
203 pub fn with_elevator(mut self, elevator: ElevatorInteraction) -> Self {
205 let elevators = self.elevators.get_or_insert_with(Vec::new);
206 elevators.push(elevator);
207 self
208 }
209
210 pub fn with_charging_station(mut self, station: ChargingStationInteraction) -> Self {
212 let stations = self.charging_stations.get_or_insert_with(Vec::new);
213 stations.push(station);
214 self
215 }
216
217 pub fn with_surface(mut self, surface: SurfaceInfo) -> Self {
219 self.surface = Some(surface);
220 self
221 }
222}
223
224impl DoorInteraction {
225 pub fn new(door_id: impl Into<String>, state: DoorState) -> Self {
227 Self {
228 door_id: Some(door_id.into()),
229 state: Some(state),
230 last_change: Some(Utc::now()),
231 ..Default::default()
232 }
233 }
234
235 pub fn waiting(mut self, wait_time_sec: f64) -> Self {
237 self.is_waiting = Some(true);
238 self.wait_time_sec = Some(wait_time_sec);
239 self
240 }
241}
242
243impl ChargingStationInteraction {
244 pub fn docked(station_id: impl Into<String>) -> Self {
246 Self {
247 station_id: Some(station_id.into()),
248 state: Some(ChargingStationState::Occupied),
249 is_docked: Some(true),
250 ..Default::default()
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn test_door_interaction() {
261 let door = DoorInteraction::new("door-001", DoorState::Closed).waiting(5.0);
262
263 assert_eq!(door.is_waiting, Some(true));
264 assert_eq!(door.wait_time_sec, Some(5.0));
265 }
266
267 #[test]
268 fn test_environment_domain() {
269 let env = EnvironmentInteractionDomain::default()
270 .with_door(DoorInteraction::new("door-001", DoorState::Open))
271 .with_charging_station(ChargingStationInteraction::docked("station-001"));
272
273 assert_eq!(env.doors.unwrap().len(), 1);
274 assert_eq!(env.charging_stations.unwrap().len(), 1);
275 }
276}