// Synapse Agriculture — Build a Farm Web Frontend // // This crate compiles to WASM and runs in the browser. // It shares synapse-core types with the sensor modules, // meaning the same Reading/TransmissionPayload types that // the MCU produces are what the dashboard renders. // // For now this is a stub proving the shared type import works. // Leptos UI comes next once the core+sensor crates stabilize. use synapse_core::{ MeasurementUnit, Reading, ReadingQuality, TransmissionPayload, }; /// Demonstrate that synapse-core types work in std/browser context. /// This will become the data layer for the Leptos dashboard. pub fn decode_payload(cbor_bytes: &[u8]) -> Result { minicbor::decode(cbor_bytes).map_err(|e| format!("CBOR decode error: {e}")) } /// Format a reading for dashboard display. /// Uses the std-only helpers from synapse-core (calibrated_f64, unit_str). pub fn format_reading(reading: &Reading) -> String { let quality_indicator = match reading.quality { ReadingQuality::Good => "", ReadingQuality::Degraded => " ⚠", ReadingQuality::CalNeeded => " 🔧", ReadingQuality::Fault => " ❌", }; format!( "{:.2} {}{}", reading.calibrated_f64(), reading.unit_str(), quality_indicator ) } #[cfg(test)] mod tests { use super::*; use synapse_core::Calibration; #[test] fn format_ph_reading() { let r = Reading { timestamp_ms: 1712345678000, channel: 0, raw_value: 1650, calibrated_value: 7230, unit: MeasurementUnit::Ph, quality: ReadingQuality::Good, }; let s = format_reading(&r); assert!(s.contains("7.23"), "expected '7.23' in '{s}'"); assert!(s.contains("pH"), "expected 'pH' in '{s}'"); } #[test] fn format_fault_reading() { let r = Reading { timestamp_ms: 0, channel: 0, raw_value: 0, calibrated_value: 0, unit: MeasurementUnit::DissolvedOxygen, quality: ReadingQuality::Fault, }; let s = format_reading(&r); assert!(s.contains("❌"), "expected fault indicator in '{s}'"); } #[test] fn cbor_roundtrip_from_simulated_node() { // Simulate what a sensor node would transmit let payload = TransmissionPayload { node_id: 1, sequence: 0, battery_mv: 3700, readings: vec![Reading { timestamp_ms: 1712345678000, channel: 0, raw_value: 1650, calibrated_value: 7230, unit: MeasurementUnit::Ph, quality: ReadingQuality::Good, }], }; // Encode (as the MCU would) let mut buf = Vec::new(); minicbor::encode(&payload, &mut buf).unwrap(); // Decode (as the browser would) let decoded = decode_payload(&buf).unwrap(); assert_eq!(decoded.node_id, 1); let formatted = format_reading(&decoded.readings[0]); assert!(formatted.contains("7.23")); } }