Files
wifi-densepose/rust-port/wifi-densepose-rs/crates/wifi-densepose-mat/src/integration/hardware_adapter.rs
Claude a17b630c02 feat: Add wifi-densepose-mat disaster detection module
Implements WiFi-Mat (Mass Casualty Assessment Tool) for detecting and
localizing survivors trapped in rubble, earthquakes, and natural disasters.

Architecture:
- Domain-Driven Design with bounded contexts (Detection, Localization, Alerting)
- Modular Rust crate integrating with existing wifi-densepose-* crates
- Event-driven architecture for audit trails and distributed deployments

Features:
- Breathing pattern detection from CSI amplitude variations
- Heartbeat detection using micro-Doppler analysis
- Movement classification (gross, fine, tremor, periodic)
- START protocol-compatible triage classification
- 3D position estimation via triangulation and depth estimation
- Real-time alert generation with priority escalation

Documentation:
- ADR-001: Architecture Decision Record for wifi-Mat
- DDD domain model specification
2026-01-13 17:24:50 +00:00

325 lines
8.7 KiB
Rust

//! Adapter for wifi-densepose-hardware crate.
use super::AdapterError;
use crate::domain::{SensorPosition, SensorType};
/// Hardware adapter for sensor communication
pub struct HardwareAdapter {
/// Connected sensors
sensors: Vec<SensorInfo>,
/// Whether hardware is initialized
initialized: bool,
}
/// Information about a connected sensor
#[derive(Debug, Clone)]
pub struct SensorInfo {
/// Unique sensor ID
pub id: String,
/// Sensor position
pub position: SensorPosition,
/// Current status
pub status: SensorStatus,
/// Last RSSI reading (if available)
pub last_rssi: Option<f64>,
/// Battery level (0-100, if applicable)
pub battery_level: Option<u8>,
}
/// Status of a sensor
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SensorStatus {
/// Sensor is connected and operational
Connected,
/// Sensor is disconnected
Disconnected,
/// Sensor is in error state
Error,
/// Sensor is initializing
Initializing,
/// Sensor battery is low
LowBattery,
}
impl HardwareAdapter {
/// Create a new hardware adapter
pub fn new() -> Self {
Self {
sensors: Vec::new(),
initialized: false,
}
}
/// Initialize hardware communication
pub fn initialize(&mut self) -> Result<(), AdapterError> {
// In production, this would initialize actual hardware
// using wifi-densepose-hardware crate
self.initialized = true;
Ok(())
}
/// Discover available sensors
pub fn discover_sensors(&mut self) -> Result<Vec<SensorInfo>, AdapterError> {
if !self.initialized {
return Err(AdapterError::Hardware("Hardware not initialized".into()));
}
// In production, this would scan for WiFi devices
// For now, return empty list (would be populated by real hardware)
Ok(Vec::new())
}
/// Add a sensor
pub fn add_sensor(&mut self, sensor: SensorInfo) -> Result<(), AdapterError> {
if self.sensors.iter().any(|s| s.id == sensor.id) {
return Err(AdapterError::Hardware(format!(
"Sensor {} already registered",
sensor.id
)));
}
self.sensors.push(sensor);
Ok(())
}
/// Remove a sensor
pub fn remove_sensor(&mut self, sensor_id: &str) -> Result<(), AdapterError> {
let initial_len = self.sensors.len();
self.sensors.retain(|s| s.id != sensor_id);
if self.sensors.len() == initial_len {
return Err(AdapterError::Hardware(format!(
"Sensor {} not found",
sensor_id
)));
}
Ok(())
}
/// Get all sensors
pub fn sensors(&self) -> &[SensorInfo] {
&self.sensors
}
/// Get operational sensors
pub fn operational_sensors(&self) -> Vec<&SensorInfo> {
self.sensors
.iter()
.filter(|s| s.status == SensorStatus::Connected)
.collect()
}
/// Get sensor positions for localization
pub fn sensor_positions(&self) -> Vec<SensorPosition> {
self.sensors
.iter()
.filter(|s| s.status == SensorStatus::Connected)
.map(|s| s.position.clone())
.collect()
}
/// Read CSI data from sensors
pub fn read_csi(&self) -> Result<CsiReadings, AdapterError> {
if !self.initialized {
return Err(AdapterError::Hardware("Hardware not initialized".into()));
}
// In production, this would read actual CSI data
// For now, return empty readings
Ok(CsiReadings {
timestamp: chrono::Utc::now(),
readings: Vec::new(),
})
}
/// Read RSSI from all sensors
pub fn read_rssi(&self) -> Result<Vec<(String, f64)>, AdapterError> {
if !self.initialized {
return Err(AdapterError::Hardware("Hardware not initialized".into()));
}
// Return last known RSSI values
Ok(self
.sensors
.iter()
.filter_map(|s| s.last_rssi.map(|rssi| (s.id.clone(), rssi)))
.collect())
}
/// Update sensor position
pub fn update_sensor_position(
&mut self,
sensor_id: &str,
position: SensorPosition,
) -> Result<(), AdapterError> {
let sensor = self
.sensors
.iter_mut()
.find(|s| s.id == sensor_id)
.ok_or_else(|| AdapterError::Hardware(format!("Sensor {} not found", sensor_id)))?;
sensor.position = position;
Ok(())
}
/// Check hardware health
pub fn health_check(&self) -> HardwareHealth {
let total = self.sensors.len();
let connected = self
.sensors
.iter()
.filter(|s| s.status == SensorStatus::Connected)
.count();
let low_battery = self
.sensors
.iter()
.filter(|s| matches!(s.battery_level, Some(b) if b < 20))
.count();
let status = if connected == 0 && total > 0 {
HealthStatus::Critical
} else if connected < total / 2 {
HealthStatus::Degraded
} else if low_battery > 0 {
HealthStatus::Warning
} else {
HealthStatus::Healthy
};
HardwareHealth {
status,
total_sensors: total,
connected_sensors: connected,
low_battery_sensors: low_battery,
}
}
}
impl Default for HardwareAdapter {
fn default() -> Self {
Self::new()
}
}
/// CSI readings from sensors
#[derive(Debug, Clone)]
pub struct CsiReadings {
/// Timestamp of readings
pub timestamp: chrono::DateTime<chrono::Utc>,
/// Individual sensor readings
pub readings: Vec<SensorCsiReading>,
}
/// CSI reading from a single sensor
#[derive(Debug, Clone)]
pub struct SensorCsiReading {
/// Sensor ID
pub sensor_id: String,
/// CSI amplitudes (per subcarrier)
pub amplitudes: Vec<f64>,
/// CSI phases (per subcarrier)
pub phases: Vec<f64>,
/// RSSI value
pub rssi: f64,
/// Noise floor
pub noise_floor: f64,
}
/// Hardware health status
#[derive(Debug, Clone)]
pub struct HardwareHealth {
/// Overall status
pub status: HealthStatus,
/// Total number of sensors
pub total_sensors: usize,
/// Number of connected sensors
pub connected_sensors: usize,
/// Number of sensors with low battery
pub low_battery_sensors: usize,
}
/// Health status levels
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HealthStatus {
/// All systems operational
Healthy,
/// Minor issues, still functional
Warning,
/// Significant issues, reduced capability
Degraded,
/// System not functional
Critical,
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_sensor(id: &str) -> SensorInfo {
SensorInfo {
id: id.to_string(),
position: SensorPosition {
id: id.to_string(),
x: 0.0,
y: 0.0,
z: 1.5,
sensor_type: SensorType::Transceiver,
is_operational: true,
},
status: SensorStatus::Connected,
last_rssi: Some(-45.0),
battery_level: Some(80),
}
}
#[test]
fn test_add_sensor() {
let mut adapter = HardwareAdapter::new();
adapter.initialize().unwrap();
let sensor = create_test_sensor("s1");
assert!(adapter.add_sensor(sensor).is_ok());
assert_eq!(adapter.sensors().len(), 1);
}
#[test]
fn test_duplicate_sensor_error() {
let mut adapter = HardwareAdapter::new();
adapter.initialize().unwrap();
let sensor1 = create_test_sensor("s1");
let sensor2 = create_test_sensor("s1");
adapter.add_sensor(sensor1).unwrap();
assert!(adapter.add_sensor(sensor2).is_err());
}
#[test]
fn test_health_check() {
let mut adapter = HardwareAdapter::new();
adapter.initialize().unwrap();
// No sensors - should be healthy (nothing to fail)
let health = adapter.health_check();
assert!(matches!(health.status, HealthStatus::Healthy));
// Add connected sensor
adapter.add_sensor(create_test_sensor("s1")).unwrap();
let health = adapter.health_check();
assert!(matches!(health.status, HealthStatus::Healthy));
}
#[test]
fn test_sensor_positions() {
let mut adapter = HardwareAdapter::new();
adapter.initialize().unwrap();
adapter.add_sensor(create_test_sensor("s1")).unwrap();
adapter.add_sensor(create_test_sensor("s2")).unwrap();
let positions = adapter.sensor_positions();
assert_eq!(positions.len(), 2);
}
}