use crate::core::{SignalOrigin, SignalValue, WorldSignal};
use crate::sensor::Sensor;
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
pub struct PhysicalSensor {
name: String,
sensor_type: PhysicalType,
physical_value: Arc<AtomicU32>, last_sent: Arc<AtomicU32>,
threshold: u32,
}
pub enum PhysicalType {
Knob {
min: f32,
max: f32,
curve: KnobCurve,
},
Button,
Switch {
positions: Vec<String>,
},
}
pub enum KnobCurve {
Linear,
Logarithmic,
Exponential,
}
impl PhysicalSensor {
pub fn knob(name: impl Into<String>) -> Self {
Self {
name: name.into(),
sensor_type: PhysicalType::Knob {
min: 0.0,
max: 1.0,
curve: KnobCurve::Linear,
},
physical_value: Arc::new(AtomicU32::new(0)),
last_sent: Arc::new(AtomicU32::new(0)),
threshold: 64, }
}
pub fn button(name: impl Into<String>) -> Self {
Self {
name: name.into(),
sensor_type: PhysicalType::Button,
physical_value: Arc::new(AtomicU32::new(0)),
last_sent: Arc::new(AtomicU32::new(0)),
threshold: 1,
}
}
pub fn with_range(mut self, min: f32, max: f32) -> Self {
if let PhysicalType::Knob { ref mut min: m, ref mut max: mx, .. } = self.sensor_type {
*m = min;
*mx = max;
}
self
}
pub fn with_curve(mut self, curve: KnobCurve) -> Self {
if let PhysicalType::Knob { ref mut curve: c, .. } = self.sensor_type {
*c = curve;
}
self
}
pub fn set_physical(&self, value: u32) {
self.physical_value.store(value & 0xFFFF, Ordering::Relaxed);
}
pub fn press(&self) {
if let PhysicalType::Button = self.sensor_type {
self.physical_value.store(1, Ordering::Relaxed);
}
}
pub fn release(&self) {
if let PhysicalType::Button = self.sensor_type {
self.physical_value.store(0, Ordering::Relaxed);
}
}
fn normalize(&self, phys: u32) -> f32 {
let norm = phys as f32 / 65535.0;
match &self.sensor_type {
PhysicalType::Knob { curve, .. } => match curve {
KnobCurve::Linear => norm,
KnobCurve::Logarithmic => {
if norm <= 0.0 { 0.0 } else { (1.0 + norm * 9.0).log10() }
}
KnobCurve::Exponential => norm * norm,
},
PhysicalType::Button => {
if phys > 0 { 1.0 } else { 0.0 }
}
PhysicalType::Switch { positions } => {
let idx = (norm * (positions.len() - 1) as f32).round() as usize;
idx as f32 / (positions.len() - 1) as f32
}
}
}
}
impl Sensor for PhysicalSensor {
fn name(&self) -> &str {
&self.name
}
fn sense(&mut self, _perception: &crate::world::Perception) -> Option<WorldSignal> {
let current = self.physical_value.load(Ordering::Relaxed);
let last = self.last_sent.load(Ordering::Relaxed);
if (current as i32 - last as i32).abs() > self.threshold as i32 {
self.last_sent.store(current, Ordering::Relaxed);
let value = self.normalize(current);
Some(WorldSignal::new(
SignalOrigin::Sensor(self.name.clone()),
SignalValue::continuous(value),
))
} else {
None
}
}
fn last_value(&self) -> f32 {
let current = self.physical_value.load(Ordering::Relaxed);
self.normalize(current)
}
}