Skip to main content

ruvix_types/
sensor.rs

1//! Sensor types for RuView perception integration.
2//!
3//! RuView sits outside the kernel but provides the perception plane.
4//! Sensors produce typed, coherence-scored events delivered via queues.
5
6use crate::handle::Handle;
7
8/// Handle to a sensor subscription.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[repr(transparent)]
11pub struct SubscriptionHandle(pub Handle);
12
13impl SubscriptionHandle {
14    /// Creates a new subscription handle.
15    #[inline]
16    #[must_use]
17    pub const fn new(id: u32, generation: u32) -> Self {
18        Self(Handle::new(id, generation))
19    }
20
21    /// Creates a null (invalid) subscription handle.
22    #[inline]
23    #[must_use]
24    pub const fn null() -> Self {
25        Self(Handle::null())
26    }
27
28    /// Checks if this handle is null.
29    #[inline]
30    #[must_use]
31    pub const fn is_null(&self) -> bool {
32        self.0.is_null()
33    }
34
35    /// Returns the raw handle.
36    #[inline]
37    #[must_use]
38    pub const fn raw(&self) -> Handle {
39        self.0
40    }
41}
42
43impl Default for SubscriptionHandle {
44    fn default() -> Self {
45        Self::null()
46    }
47}
48
49/// Type of sensor data source.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51#[repr(u8)]
52pub enum SensorType {
53    /// Camera/video sensor.
54    Camera = 0,
55
56    /// Microphone/audio sensor.
57    Microphone = 1,
58
59    /// Network traffic tap.
60    NetworkTap = 2,
61
62    /// Financial market data feed.
63    MarketFeed = 3,
64
65    /// Git repository event stream.
66    GitStream = 4,
67
68    /// File system change events.
69    FileSystem = 5,
70
71    /// System metrics (CPU, memory, etc.).
72    SystemMetrics = 6,
73
74    /// Custom/user-defined sensor type.
75    Custom = 255,
76}
77
78impl SensorType {
79    /// Returns the sensor type as a string.
80    #[inline]
81    #[must_use]
82    pub const fn as_str(&self) -> &'static str {
83        match self {
84            Self::Camera => "Camera",
85            Self::Microphone => "Microphone",
86            Self::NetworkTap => "NetworkTap",
87            Self::MarketFeed => "MarketFeed",
88            Self::GitStream => "GitStream",
89            Self::FileSystem => "FileSystem",
90            Self::SystemMetrics => "SystemMetrics",
91            Self::Custom => "Custom",
92        }
93    }
94
95    /// Converts from a raw u8 value.
96    #[inline]
97    #[must_use]
98    pub const fn from_u8(value: u8) -> Option<Self> {
99        match value {
100            0 => Some(Self::Camera),
101            1 => Some(Self::Microphone),
102            2 => Some(Self::NetworkTap),
103            3 => Some(Self::MarketFeed),
104            4 => Some(Self::GitStream),
105            5 => Some(Self::FileSystem),
106            6 => Some(Self::SystemMetrics),
107            255 => Some(Self::Custom),
108            _ => None,
109        }
110    }
111}
112
113impl Default for SensorType {
114    fn default() -> Self {
115        Self::Custom
116    }
117}
118
119/// Sensor descriptor identifying a data source.
120///
121/// Used in `sensor_subscribe` to specify which sensor to monitor.
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123#[repr(C)]
124pub struct SensorDescriptor {
125    /// Type of sensor.
126    pub sensor_type: SensorType,
127
128    /// Device identifier (hardware address, URL hash, stream ID, etc.).
129    /// Interpretation depends on sensor_type.
130    pub device_id: u64,
131
132    /// Filter expression hash (0 = no filter).
133    /// The actual filter is stored in a region and referenced by hash.
134    pub filter_hash: u64,
135
136    /// Requested sampling rate (events per second).
137    /// 0 = all events (no downsampling).
138    pub sample_rate: u32,
139}
140
141impl SensorDescriptor {
142    /// Creates a new sensor descriptor.
143    #[inline]
144    #[must_use]
145    pub const fn new(sensor_type: SensorType, device_id: u64) -> Self {
146        Self {
147            sensor_type,
148            device_id,
149            filter_hash: 0,
150            sample_rate: 0,
151        }
152    }
153
154    /// Creates a descriptor with sampling rate.
155    #[inline]
156    #[must_use]
157    pub const fn with_sample_rate(mut self, rate: u32) -> Self {
158        self.sample_rate = rate;
159        self
160    }
161
162    /// Creates a descriptor with a filter.
163    #[inline]
164    #[must_use]
165    pub const fn with_filter(mut self, filter_hash: u64) -> Self {
166        self.filter_hash = filter_hash;
167        self
168    }
169
170    /// Returns true if downsampling is enabled.
171    #[inline]
172    #[must_use]
173    pub const fn is_downsampled(&self) -> bool {
174        self.sample_rate > 0
175    }
176
177    /// Returns true if a filter is applied.
178    #[inline]
179    #[must_use]
180    pub const fn has_filter(&self) -> bool {
181        self.filter_hash != 0
182    }
183}
184
185impl Default for SensorDescriptor {
186    fn default() -> Self {
187        Self {
188            sensor_type: SensorType::Custom,
189            device_id: 0,
190            filter_hash: 0,
191            sample_rate: 0,
192        }
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_subscription_handle() {
202        let h = SubscriptionHandle::new(3, 5);
203        assert!(!h.is_null());
204        assert_eq!(h.raw().id, 3);
205    }
206
207    #[test]
208    fn test_sensor_type_roundtrip() {
209        for i in 0..=6 {
210            let st = SensorType::from_u8(i).unwrap();
211            assert_eq!(st as u8, i);
212        }
213        assert!(SensorType::from_u8(100).is_none());
214        assert_eq!(SensorType::from_u8(255), Some(SensorType::Custom));
215    }
216
217    #[test]
218    fn test_sensor_descriptor() {
219        let desc = SensorDescriptor::new(SensorType::Camera, 12345)
220            .with_sample_rate(30)
221            .with_filter(0xABCD);
222
223        assert!(desc.is_downsampled());
224        assert!(desc.has_filter());
225        assert_eq!(desc.sample_rate, 30);
226    }
227}