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
13pub 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: u32 = 0x000001;
43pub const TAG_METEOROLOGICAL: u32 = 0x000002;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[repr(C)]
48pub struct SemanticAtom {
49 pub entity_id: u32,
50 pub telemetry_info: u32,
51 pub predicate_id: u32,
52 pub value_mm: u32,
53 pub timestamp_us: u64,
54 pub tag_trust: u32,
55 pub pqc_anchor: u32,
56}
57
58impl SemanticAtom {
59 #[inline]
60 pub fn new(
61 entity_id: u32,
62 telemetry_type: u16,
63 predicate_id: u32,
64 value_mm: u32,
65 timestamp_us: u64,
66 tag_id: u32,
67 trust_level: u8,
68 ) -> Self {
69 let telemetry_info = (telemetry_type as u32) << 16;
70 let tag_trust = ((tag_id & 0x00FFFFFF) << 8) | (trust_level as u32);
71
72 Self {
73 entity_id,
74 telemetry_info,
75 predicate_id,
76 value_mm,
77 timestamp_us,
78 tag_trust,
79 pqc_anchor: 0,
80 }
81 }
82
83 #[inline]
84 pub fn telemetry_type(&self) -> u16 {
85 (self.telemetry_info >> 16) as u16
86 }
87 #[inline]
88 pub fn tag_id(&self) -> u32 {
89 self.tag_trust >> 8
90 }
91 #[inline]
92 pub fn trust_level(&self) -> u8 {
93 (self.tag_trust & 0xFF) as u8
94 }
95 #[inline]
96 pub fn set_pqc_anchor(&mut self, anchor: u32) {
97 self.pqc_anchor = anchor;
98 }
99
100 #[inline]
101 pub fn is_critical_or_warning(&self) -> bool {
102 matches!(
103 self.predicate_id,
104 PREDICATE_WARNING
105 | PREDICATE_CRITICAL
106 | PREDICATE_API_DATA_INCONSISTENCY
107 | PREDICATE_POTENTIAL_DATA_ANOMALY
108 )
109 }
110
111 #[inline]
112 pub fn get_value(&self) -> f64 {
113 self.value_mm as f64 / 100.0
114 }
115
116 #[inline]
117 pub fn to_bytes(&self) -> [u8; 32] {
118 let mut bytes = [0u8; 32];
119 bytes[0..4].copy_from_slice(&self.entity_id.to_le_bytes());
120 bytes[4..8].copy_from_slice(&self.telemetry_info.to_le_bytes());
121 bytes[8..12].copy_from_slice(&self.predicate_id.to_le_bytes());
122 bytes[12..16].copy_from_slice(&self.value_mm.to_le_bytes());
123 bytes[16..24].copy_from_slice(&self.timestamp_us.to_le_bytes());
124 bytes[24..28].copy_from_slice(&self.tag_trust.to_le_bytes());
125 bytes[28..32].copy_from_slice(&self.pqc_anchor.to_le_bytes());
126 bytes
127 }
128
129 pub fn from_bytes(bytes: &[u8]) -> Result<Self, &'static str> {
130 if bytes.len() != 32 {
131 return Err("Invalid length");
132 }
133 Ok(Self {
134 entity_id: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
135 telemetry_info: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
136 predicate_id: u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
137 value_mm: u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
138 timestamp_us: u64::from_le_bytes([
139 bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22],
140 bytes[23],
141 ]),
142 tag_trust: u32::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
143 pqc_anchor: u32::from_le_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
144 })
145 }
146
147 #[cfg(feature = "pqc")]
148 pub fn compute_hash(&self) -> [u8; 32] {
149 use sha2::{Digest, Sha256};
150 let mut hasher = Sha256::new();
151 hasher.update(self.to_bytes());
152 hasher.finalize().into()
153 }
154}
155
156#[derive(Debug, Clone, PartialEq)]
159#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
160pub enum AtomError {
161 InvalidAtom(&'static str),
162 InvalidEntity(u32),
163 InvalidPredicate(u32),
164 PhysicsViolation(&'static str),
165 InvalidTimestamp,
166 CryptographicError(&'static str),
167 InvalidLength(usize),
168}
169
170impl core::fmt::Display for AtomError {
171 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172 match self {
173 AtomError::InvalidAtom(msg) => write!(f, "Invalid atom: {}", msg),
174 AtomError::InvalidEntity(id) => write!(f, "Invalid entity ID: 0x{:08X}", id),
175 AtomError::InvalidPredicate(id) => write!(f, "Invalid predicate ID: 0x{:08X}", id),
176 AtomError::PhysicsViolation(msg) => write!(f, "Physics violation: {}", msg),
177 AtomError::InvalidTimestamp => write!(f, "Invalid timestamp"),
178 AtomError::CryptographicError(msg) => write!(f, "Cryptographic error: {}", msg),
179 AtomError::InvalidLength(len) => write!(f, "Invalid length: {} (expected 32)", len),
180 }
181 }
182}
183
184#[cfg(feature = "std")]
185impl std::error::Error for AtomError {}
186
187pub type BsaResult<T> = Result<T, AtomError>;
188
189#[cfg(feature = "serde")]
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct Entity {
194 pub id: u32,
195 pub name: String,
196 pub description: String,
197 pub source: Option<String>,
198}
199
200#[cfg(feature = "serde")]
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct Predicate {
203 pub id: u32,
204 pub name: String,
205 pub description: String,
206 pub tag: String,
207}
208
209#[cfg(feature = "serde")]
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Config {
212 pub entities: Vec<Entity>,
213 pub predicates: Vec<Predicate>,
214}