1use super::*;
5use std::collections::HashMap;
6use std::thread;
7use std::time::Duration;
8
9pub struct AmbientOrchestrationManager {
11 pub(super) devices: HashMap<String, AmbientDevice>,
12 orchestrator: SubThresholdOrchestrator,
13 power_manager: PowerManager,
14 performance_monitor: AmbientPerformanceMonitor,
15 task_scheduler: TaskScheduler,
16}
17
18pub struct SubThresholdOrchestrator {
20 orchestration_policy: OrchestrationPolicy,
21 workload_analyzer: WorkloadAnalyzer,
22 resource_allocator: ResourceAllocator,
23 adaptation_engine: AdaptationEngine,
24}
25
26impl AmbientOrchestrationManager {
27 pub fn new() -> Self {
29 Self {
30 devices: HashMap::new(),
31 orchestrator: SubThresholdOrchestrator::new(),
32 power_manager: PowerManager::new(),
33 performance_monitor: AmbientPerformanceMonitor::new(),
34 task_scheduler: TaskScheduler::new(),
35 }
36 }
37
38 pub fn register_device(&mut self, device: AmbientDevice) -> Result<(), AmbientError> {
40 self.validate_device(&device)?;
42
43 self.devices.insert(device.device_id.clone(), device);
45
46 Ok(())
47 }
48
49 pub fn discover_devices(&mut self) -> Result<Vec<String>, AmbientError> {
51 let mut handles = [AmbientDeviceHandle {
52 device_id_hash: 0,
53 device_type: DeviceType::Embedded,
54 compute_units: 0,
55 memory_size: 0,
56 state: DeviceState::Offline,
57 }; 9];
58 let written = self.discover_devices_into(&mut handles)?;
59 let mut discovered = Vec::with_capacity(written);
60 for handle in handles.into_iter().take(written) {
61 if handle.device_id_hash == crate::q_hash("local_host") {
62 discovered.push("local_host".to_string());
63 } else {
64 discovered.push(format!("cpu_core_{}", discovered.len().saturating_sub(1)));
65 }
66 }
67 Ok(discovered)
68 }
69
70 pub fn discover_devices_into(
72 &mut self,
73 out: &mut [AmbientDeviceHandle],
74 ) -> Result<usize, AmbientError> {
75 use sysinfo::System;
76
77 let mut sys = System::new_all();
81 sys.refresh_all();
82 let mut discovered = 0usize;
83 self.devices.clear();
84
85 let cpus = sys.cpus();
86 let cpu_count = cpus.len().max(1);
87 let cpu_brand = cpus
88 .first()
89 .map(|c| c.brand().to_string())
90 .unwrap_or_else(|| "Unknown CPU".to_string());
91 let base_freq_mhz = cpus.first().map(|c| c.frequency()).unwrap_or(1000);
92 let total_mem = sys.total_memory(); let host_id = "local_host".to_string();
96 let host = AmbientDevice {
97 device_id: host_id.clone(),
98 device_type: DeviceType::Embedded,
99 capabilities: DeviceCapabilities {
100 neural_engines: vec![NeuralEngine::ONNXRuntime],
101 compute_units: cpu_count as u32,
102 memory_size: total_mem,
103 battery_capacity: 0,
104 thermal_limit: 95.0,
105 supported_frameworks: vec![Framework::ONNX, Framework::Custom(cpu_brand.clone())],
106 },
107 current_state: DeviceState::Active,
108 performance_profile: PerformanceProfile {
109 peak_performance: base_freq_mhz as f64 * cpu_count as f64 / 1000.0,
110 sustainable_performance: base_freq_mhz as f64 * cpu_count as f64 * 0.8 / 1000.0,
111 thermal_performance: base_freq_mhz as f64 * cpu_count as f64 * 0.6 / 1000.0,
112 battery_performance: 1.0,
113 efficiency_factor: 0.90,
114 },
115 power_profile: PowerProfile {
116 baseline_power: 5.0 * cpu_count as f64,
117 active_power: 15.0 * cpu_count as f64,
118 peak_power: 35.0 * cpu_count as f64,
119 idle_power: 1.0,
120 sleep_power: 0.5,
121 },
122 };
123 if self.register_device(host).is_ok() {
124 if discovered >= out.len() {
125 return Err(AmbientError::InsufficientResources(
126 "device output buffer full".to_string(),
127 ));
128 }
129 out[discovered] = self.snapshot_device_handle("local_host").unwrap();
130 discovered += 1;
131 }
132
133 for i in 0..cpu_count.min(8) {
135 let core_freq = cpus.get(i).map(|c| c.frequency()).unwrap_or(base_freq_mhz);
136 let core_id = format!("cpu_core_{}", i);
137 let core = AmbientDevice {
138 device_id: core_id.clone(),
139 device_type: DeviceType::Embedded,
140 capabilities: DeviceCapabilities {
141 neural_engines: vec![NeuralEngine::ONNXRuntime],
142 compute_units: 1,
143 memory_size: total_mem / cpu_count as u64,
144 battery_capacity: 0,
145 thermal_limit: 95.0,
146 supported_frameworks: vec![Framework::ONNX],
147 },
148 current_state: DeviceState::Active,
149 performance_profile: PerformanceProfile {
150 peak_performance: core_freq as f64 / 1000.0,
151 sustainable_performance: core_freq as f64 * 0.8 / 1000.0,
152 thermal_performance: core_freq as f64 * 0.6 / 1000.0,
153 battery_performance: 1.0,
154 efficiency_factor: 0.85,
155 },
156 power_profile: PowerProfile {
157 baseline_power: 5.0,
158 active_power: 15.0,
159 peak_power: 35.0,
160 idle_power: 1.0,
161 sleep_power: 0.5,
162 },
163 };
164 if self.register_device(core).is_ok() {
165 if discovered >= out.len() {
166 return Err(AmbientError::InsufficientResources(
167 "device output buffer full".to_string(),
168 ));
169 }
170 out[discovered] = self.snapshot_device_handle(&core_id).unwrap();
171 discovered += 1;
172 }
173 }
174
175 Ok(discovered)
176 }
177
178 pub fn submit_task(&mut self, task: Task) -> Result<String, AmbientError> {
180 self.validate_task(&task)?;
182
183 self.task_scheduler.submit_task(task.clone())?;
185
186 Ok(task.task_id.clone())
187 }
188
189 pub fn execute_neural_inference(
191 &mut self,
192 device_id: &str,
193 model_data: &[u8],
194 input_data: &[u8],
195 ) -> Result<Vec<u8>, AmbientError> {
196 let mut out = vec![0u8; 1024];
197 let written =
198 self.execute_neural_inference_into(device_id, model_data, input_data, &mut out)?;
199 out.truncate(written);
200 Ok(out)
201 }
202
203 pub fn execute_neural_inference_into(
205 &mut self,
206 device_id: &str,
207 model_data: &[u8],
208 input_data: &[u8],
209 out: &mut [u8],
210 ) -> Result<usize, AmbientError> {
211 let device = self
213 .devices
214 .get(device_id)
215 .ok_or_else(|| AmbientError::DeviceNotFound(device_id.to_string()))?
216 .clone();
217 self.execute_inference_on_device(&device, model_data, input_data, out)
218 }
219
220 pub fn execute_sub_threshold_computation(
222 &mut self,
223 device_id: &str,
224 computation: SubThresholdComputation,
225 ) -> Result<ComputationResult, AmbientError> {
226 let device = self
228 .devices
229 .get(device_id)
230 .ok_or_else(|| AmbientError::DeviceNotFound(device_id.to_string()))?
231 .clone();
232 self.execute_computation_on_device(&device, &computation)
233 }
234
235 pub fn get_device_status(&self, device_id: &str) -> Option<DeviceStatus> {
237 self.devices.get(device_id).map(|device| DeviceStatus {
238 device_id: device.device_id.clone(),
239 device_type: device.device_type.clone(),
240 state: device.current_state.clone(),
241 battery_level: self.power_manager.get_battery_level(device_id),
242 thermal_state: self.power_manager.get_thermal_state(device_id),
243 performance: device.performance_profile.clone(),
244 power_consumption: self.power_manager.get_power_consumption(device_id),
245 })
246 }
247
248 pub fn get_performance_stats(&self) -> AmbientGlobalMetrics {
250 self.performance_monitor.get_global_stats()
251 }
252
253 pub fn set_orchestration_state(&mut self, state: AmbientOrchestrationState) {
259 self.power_manager.set_orchestration_state(state);
260 }
261
262 pub fn set_active_model_count(&mut self, count: usize) {
264 self.power_manager.set_active_model_count(count);
265 }
266
267 pub fn get_power_metrics(&self) -> PowerMetrics {
269 self.power_manager.get_power_metrics()
270 }
271
272 pub fn estimate_battery_life_remaining(
275 &self,
276 current_battery_pct: f64,
277 battery_capacity_wh: f64,
278 ) -> f64 {
279 self.power_manager
280 .estimate_battery_life_remaining(current_battery_pct, battery_capacity_wh)
281 }
282
283 pub fn should_throttle_inference(&self) -> bool {
285 self.power_manager.should_throttle_inference()
286 }
287
288 pub fn list_devices(&self) -> Vec<String> {
290 self.devices.keys().cloned().collect()
291 }
292
293 pub fn list_devices_into(
294 &self,
295 out: &mut [AmbientDeviceHandle],
296 ) -> Result<usize, AmbientError> {
297 if out.len() < self.devices.len() {
298 return Err(AmbientError::InsufficientResources(
299 "device output buffer full".to_string(),
300 ));
301 }
302
303 let mut written = 0usize;
304 for device_id in self.devices.keys() {
305 out[written] = self.snapshot_device_handle(device_id).unwrap();
306 written += 1;
307 }
308 Ok(written)
309 }
310
311 pub fn get_pending_tasks(&self) -> Vec<Task> {
313 self.task_scheduler.get_pending_tasks()
314 }
315
316 pub fn get_pending_tasks_into(&self, out: &mut [TaskHandle]) -> Result<usize, AmbientError> {
317 self.task_scheduler.get_pending_tasks_into(out)
318 }
319
320 pub fn optimize_orchestration(&mut self) -> Result<(), AmbientError> {
322 let history = self.task_scheduler.recent_history(10);
324 for record in history {
325 let sample = WorkloadSample {
326 timestamp: record.end_time,
327 cpu_usage: record.resource_usage.compute_units_used as f64 / 100.0,
328 memory_usage: record.resource_usage.memory_used as f64 / (1024.0 * 1024.0),
329 neural_engine_usage: record.resource_usage.neural_engines_used as f64,
330 power_consumption: record.resource_usage.power_consumed,
331 thermal_state: record.resource_usage.thermal_impact,
332 battery_level: self.power_manager.get_battery_level(&record.device_id),
333 };
334 self.orchestrator.workload_analyzer.record_sample(sample);
335 }
336
337 let workload_analysis = self.orchestrator.workload_analyzer.analyze_workload();
339
340 let new_policy = self
342 .orchestrator
343 .adaptation_engine
344 .adapt_policy(workload_analysis);
345
346 let adjusted_policy = match self.power_manager.power_policy() {
348 PowerPolicy::PowerSaving | PowerPolicy::UltraPowerSaving => {
349 OrchestrationPolicy::BatteryAware
350 }
351 _ => new_policy,
352 };
353
354 self.orchestrator.orchestration_policy = adjusted_policy;
356
357 Ok(())
358 }
359
360 fn validate_device(&self, device: &AmbientDevice) -> Result<(), AmbientError> {
364 if device.device_id.is_empty() {
365 return Err(AmbientError::InvalidDevice(
366 "Device ID cannot be empty".to_string(),
367 ));
368 }
369
370 if device.capabilities.neural_engines.is_empty() {
371 return Err(AmbientError::InvalidDevice(
372 "Device must have at least one neural engine".to_string(),
373 ));
374 }
375
376 Ok(())
377 }
378
379 fn validate_task(&self, task: &Task) -> Result<(), AmbientError> {
381 if task.task_id.is_empty() {
382 return Err(AmbientError::InvalidTask(
383 "Task ID cannot be empty".to_string(),
384 ));
385 }
386
387 if task.resource_requirements.compute_units == 0 {
388 return Err(AmbientError::InvalidTask(
389 "Task must require at least one compute unit".to_string(),
390 ));
391 }
392
393 Ok(())
394 }
395
396 fn execute_inference_on_device(
398 &self,
399 device: &AmbientDevice,
400 model_data: &[u8],
401 input_data: &[u8],
402 out: &mut [u8],
403 ) -> Result<usize, AmbientError> {
404 let _ = (device, model_data, input_data);
407 thread::sleep(Duration::from_millis(100)); if out.len() < 1024 {
410 return Err(AmbientError::InsufficientResources(
411 "inference output buffer too small".to_string(),
412 ));
413 }
414 out[..1024].fill(0);
415 Ok(1024)
416 }
417
418 fn execute_computation_on_device(
420 &self,
421 _device: &AmbientDevice,
422 _computation: &SubThresholdComputation,
423 ) -> Result<ComputationResult, AmbientError> {
424 thread::sleep(Duration::from_millis(50)); Ok(ComputationResult {
429 result_data: vec![0u8; 512],
430 execution_time: Duration::from_millis(50),
431 power_consumed: 0.1,
432 thermal_impact: 0.5,
433 })
434 }
435
436 fn snapshot_device_handle(&self, device_id: &str) -> Option<AmbientDeviceHandle> {
437 self.devices
438 .get(device_id)
439 .map(|device| AmbientDeviceHandle {
440 device_id_hash: crate::q_hash(&device.device_id),
441 device_type: device.device_type.clone(),
442 compute_units: device.capabilities.compute_units,
443 memory_size: device.capabilities.memory_size,
444 state: device.current_state.clone(),
445 })
446 }
447}
448
449impl SubThresholdOrchestrator {
450 pub fn new() -> Self {
452 Self {
453 orchestration_policy: OrchestrationPolicy::Adaptive,
454 workload_analyzer: WorkloadAnalyzer::new(),
455 resource_allocator: ResourceAllocator::new(),
456 adaptation_engine: AdaptationEngine::new(),
457 }
458 }
459
460 pub fn optimize_for_sub_threshold(
470 &mut self,
471 computation: SubThresholdComputation,
472 ) -> SubThresholdComputation {
473 let mut optimized = computation;
474
475 let (compute_factor, power_factor, thermal_factor) = match self.orchestration_policy {
476 OrchestrationPolicy::PowerEfficiency => (0.50, 0.40, 0.50),
477 OrchestrationPolicy::ThermalAware => (0.60, 0.50, 0.60),
478 OrchestrationPolicy::BatteryAware => (0.40, 0.30, 0.50),
479 OrchestrationPolicy::PerformanceFirst => (0.90, 0.80, 0.80),
480 OrchestrationPolicy::Adaptive => (0.70, 0.50, 0.60),
481 };
482
483 let available = self.resource_allocator.available_compute_units();
485 let requested = optimized.resource_requirements.compute_units;
486 let scaled = (requested as f64 * compute_factor) as u32;
487 optimized.resource_requirements.compute_units = scaled.min(available);
489 optimized.resource_requirements.power_budget *= power_factor;
490 optimized.resource_requirements.thermal_budget *= thermal_factor;
491
492 optimized
493 }
494
495 pub fn workload_analyzer_mut(&mut self) -> &mut WorkloadAnalyzer {
497 &mut self.workload_analyzer
498 }
499
500 pub fn resource_allocator_mut(&mut self) -> &mut ResourceAllocator {
502 &mut self.resource_allocator
503 }
504}