phytrace_sdk/models/domains/
thermal.rs1use serde::{Deserialize, Serialize};
4use validator::Validate;
5
6use crate::models::enums::{CoolingMode, ThermalState};
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct ThermalDomain {
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub thermal_state: Option<ThermalState>,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub components: Option<Vec<ThermalComponent>>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub ambient_temp_c: Option<f64>,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub cooling_system: Option<CoolingSystem>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub heating_system: Option<HeatingSystem>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub is_throttling: Option<bool>,
34}
35
36#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
38pub struct ThermalComponent {
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub name: Option<String>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub component_type: Option<String>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub temperature_c: Option<f64>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub warning_threshold_c: Option<f64>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub critical_threshold_c: Option<f64>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub state: Option<ThermalState>,
62}
63
64#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
66pub struct CoolingSystem {
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub mode: Option<CoolingMode>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub fan_speeds: Option<Vec<FanInfo>>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub coolant_temp_c: Option<f64>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub coolant_flow_lpm: Option<f64>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub is_operational: Option<bool>,
86}
87
88#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
90pub struct FanInfo {
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub name: Option<String>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub speed_rpm: Option<u32>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 #[validate(range(min = 0.0, max = 100.0))]
102 pub speed_pct: Option<f64>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub is_operational: Option<bool>,
107}
108
109#[derive(Debug, Clone, Default, Serialize, Deserialize)]
111pub struct HeatingSystem {
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub is_active: Option<bool>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub target_temp_c: Option<f64>,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub power_w: Option<f64>,
123}
124
125impl ThermalDomain {
126 pub fn new(state: ThermalState) -> Self {
128 Self {
129 thermal_state: Some(state),
130 is_throttling: Some(state == ThermalState::High || state == ThermalState::Critical),
131 ..Default::default()
132 }
133 }
134
135 pub fn with_component(mut self, component: ThermalComponent) -> Self {
137 let components = self.components.get_or_insert_with(Vec::new);
138 components.push(component);
139 self
140 }
141
142 pub fn with_ambient(mut self, temp_c: f64) -> Self {
144 self.ambient_temp_c = Some(temp_c);
145 self
146 }
147
148 pub fn with_cooling(mut self, cooling: CoolingSystem) -> Self {
150 self.cooling_system = Some(cooling);
151 self
152 }
153}
154
155impl ThermalComponent {
156 pub fn new(name: impl Into<String>, temp_c: f64) -> Self {
158 let state = if temp_c > 90.0 {
159 ThermalState::Critical
160 } else if temp_c > 75.0 {
161 ThermalState::High
162 } else if temp_c > 60.0 {
163 ThermalState::Elevated
164 } else {
165 ThermalState::Normal
166 };
167
168 Self {
169 name: Some(name.into()),
170 temperature_c: Some(temp_c),
171 state: Some(state),
172 ..Default::default()
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_thermal_component() {
183 let comp = ThermalComponent::new("CPU", 65.0);
184 assert_eq!(comp.state, Some(ThermalState::Elevated));
185 }
186
187 #[test]
188 fn test_thermal_domain() {
189 let thermal = ThermalDomain::new(ThermalState::Normal)
190 .with_component(ThermalComponent::new("battery", 35.0))
191 .with_ambient(25.0);
192
193 assert_eq!(thermal.components.unwrap().len(), 1);
194 }
195}