Skip to main content

omwei_atom/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4#[cfg(all(not(feature = "std"), feature = "serde"))]
5extern crate alloc;
6
7#[cfg(all(not(feature = "std"), feature = "serde"))]
8use alloc::{string::String, vec::Vec};
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13// --- [Constants stay the same as in your version] ---
14pub const TRUST_RAW: u8 = 0x00;
15pub const TRUST_VERIFIED: u8 = 0x01;
16pub const TRUST_ANOMALY: u8 = 0x02;
17pub const TRUST_ENTERPRISE: u8 = 0xFF;
18
19pub const PREDICATE_NORMAL: u32 = 0x00000001;
20pub const PREDICATE_WARNING: u32 = 0x00000002;
21pub const PREDICATE_CRITICAL: u32 = 0x00000003;
22pub const PREDICATE_CONSISTENCY: u32 = 0x00000004;
23pub const PREDICATE_TRIANGULATED: u32 = 0x00000005;
24pub const PREDICATE_ANOMALY: u32 = 0x00000006;
25pub const PREDICATE_TREND_STABLE: u32 = 0x00000007;
26pub const PREDICATE_TREND_RISING_FAST: u32 = 0x00000008;
27pub const PREDICATE_SENSOR_STUCK_WARNING: u32 = 0x00000009;
28pub const PREDICATE_API_DATA_INCONSISTENCY: u32 = 0x0000000A;
29pub const PREDICATE_POTENTIAL_DATA_ANOMALY: u32 = 0x0000000B;
30
31pub const TELEMETRY_WATER_LEVEL_MM: u16 = 0x0001;
32pub const TELEMETRY_TEMPERATURE_C: u16 = 0x0002;
33pub const TELEMETRY_HUMIDITY_PERCENT: u16 = 0x0003;
34pub const TELEMETRY_PRESSURE_PA: u16 = 0x0004;
35pub const TELEMETRY_PRECIPITATION_MM: u16 = 0x0005;
36
37pub const HEADER_VERIFIED: u16 = 0x8000;
38pub const HEADER_SIGNED: u16 = 0x4000;
39pub const HEADER_ENCRYPTED: u16 = 0x2000;
40pub const HEADER_RAW_DATA: u16 = 0x0001;
41
42pub const TAG_HYDROLOGICAL: &str = "hydrological";
43pub const TAG_METEOROLOGICAL: &str = "meteorological";
44
45// Entity IDs
46pub const ENTITY_EA_STATION: u32 = 0x00000001;
47pub const ENTITY_YR_NO_PRECIPITATION: u32 = 0x00000002;
48pub const ENTITY_OWM_WEATHER: u32 = 0x00000003;
49pub const VIRTUAL_STATION_ARMLEY: u32 = 0x00000004;
50pub const VIRTUAL_STATION_CROWN_POINT: u32 = 0x00000005;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54#[repr(C)]
55pub struct SemanticAtom {
56    pub entity_id: u32,
57    pub telemetry_info: u32,
58    pub predicate_id: u32,
59    pub value_mm: u32,
60    pub timestamp_us: u64,
61    pub tag_trust: u32,
62    pub pqc_anchor: u32,
63}
64
65impl SemanticAtom {
66    #[inline]
67    pub fn new(
68        entity_id: u32,
69        telemetry_type: u16,
70        predicate_id: u32,
71        value_mm: u32,
72        timestamp_us: u64,
73        tag_id: u32,
74        trust_level: u8,
75    ) -> Self {
76        let telemetry_info = ((telemetry_type as u32) << 16);
77        let tag_trust = ((tag_id & 0x00FFFFFF) << 8) | (trust_level as u32);
78        
79        Self {
80            entity_id,
81            telemetry_info,
82            predicate_id,
83            value_mm,
84            timestamp_us,
85            tag_trust,
86            pqc_anchor: 0,
87        }
88    }
89
90    #[inline] pub fn telemetry_type(&self) -> u16 { (self.telemetry_info >> 16) as u16 }
91    #[inline] pub fn tag_id(&self) -> u32 { self.tag_trust >> 8 }
92    #[inline] pub fn trust_level(&self) -> u8 { (self.tag_trust & 0xFF) as u8 }
93    #[inline] pub fn set_pqc_anchor(&mut self, anchor: u32) { self.pqc_anchor = anchor; }
94    
95    #[inline]
96    pub fn is_critical_or_warning(&self) -> bool {
97        matches!(self.predicate_id, 
98            PREDICATE_WARNING | PREDICATE_CRITICAL |
99            PREDICATE_API_DATA_INCONSISTENCY | PREDICATE_POTENTIAL_DATA_ANOMALY
100        )
101    }
102
103    #[inline] pub fn get_value(&self) -> f64 { self.value_mm as f64 / 100.0 }
104
105    #[inline]
106    pub fn to_bytes(&self) -> [u8; 32] {
107        let mut bytes = [0u8; 32];
108        bytes[0..4].copy_from_slice(&self.entity_id.to_le_bytes());
109        bytes[4..8].copy_from_slice(&self.telemetry_info.to_le_bytes());
110        bytes[8..12].copy_from_slice(&self.predicate_id.to_le_bytes());
111        bytes[12..16].copy_from_slice(&self.value_mm.to_le_bytes());
112        bytes[16..24].copy_from_slice(&self.timestamp_us.to_le_bytes());
113        bytes[24..28].copy_from_slice(&self.tag_trust.to_le_bytes());
114        bytes[28..32].copy_from_slice(&self.pqc_anchor.to_le_bytes());
115        bytes
116    }
117
118    pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
119        if bytes.len() != 32 { return Err("Invalid length"); }
120        Ok(Self {
121            entity_id: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
122            telemetry_info: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
123            predicate_id: u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
124            value_mm: u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
125            timestamp_us: u64::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]),
126            tag_trust: u32::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
127            pqc_anchor: u32::from_le_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
128        })
129    }
130
131    #[cfg(feature = "pqc")]
132    pub fn compute_hash(&self) -> [u8; 32] {
133        use sha2::{Sha256, Digest};
134        let mut hasher = Sha256::new();
135        hasher.update(self.to_bytes());
136        hasher.finalize().into()
137    }
138}
139
140// --- [Error Handling - Fixed for no_std] ---
141
142#[derive(Debug, Clone, PartialEq)]
143#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
144pub enum AtomError {
145    InvalidAtom(String),
146    InvalidEntity(u32),
147    InvalidPredicate(u32),
148    PhysicsViolation(String),
149    InvalidTimestamp,
150    CryptographicError(String),
151    InvalidLength(usize),
152}
153
154impl core::fmt::Display for AtomError {
155    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156        match self {
157            AtomError::InvalidAtom(msg) => write!(f, "Invalid atom: {}", msg),
158            AtomError::InvalidEntity(id) => write!(f, "Invalid entity ID: 0x{:08X}", id),
159            AtomError::InvalidPredicate(id) => write!(f, "Invalid predicate ID: 0x{:08X}", id),
160            AtomError::PhysicsViolation(msg) => write!(f, "Physics violation: {}", msg),
161            AtomError::InvalidTimestamp => write!(f, "Invalid timestamp"),
162            AtomError::CryptographicError(msg) => write!(f, "Cryptographic error: {}", msg),
163            AtomError::InvalidLength(len) => write!(f, "Invalid length: {} (expected 32)", len),
164        }
165    }
166}
167
168#[cfg(feature = "std")]
169impl std::error::Error for AtomError {}
170
171pub type BsaResult<T> = Result<T, AtomError>;
172
173// --- [Optional Helper Structures - Fixed for no_std/serde] ---
174
175#[cfg(feature = "serde")]
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct Entity {
178    pub id: u32,
179    pub name: String,
180    pub description: String,
181    pub source: Option<String>,
182}
183
184#[cfg(feature = "serde")]
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct Predicate {
187    pub id: u32,
188    pub name: String,
189    pub description: String,
190    pub tag: String,
191}
192
193#[cfg(feature = "serde")]
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct Config {
196    pub entities: Vec<Entity>,
197    pub predicates: Vec<Predicate>,
198}