diff --git a/.cargo/config b/.cargo/config index 382c36e..a765d53 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "gdb-multiarch -q -x openocd.gdb" +runner = "gdb-multiarch -q -x bmp.gdb" rustflags = ["-C", "link-arg=-Tlink.x"] [build] diff --git a/Cargo.toml b/Cargo.toml index acaad2f..7522e17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ log = "0.4" panic-semihosting = { version = "0.5", optional = true } panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } -heapless = "0.5" +heapless = "0.5.4" serde-json-core = "0.1" cortex-m-rtfm = "0.5" embedded-hal = "0.2.3" diff --git a/src/afe.rs b/src/afe.rs index c3d9f3c..8dabf06 100644 --- a/src/afe.rs +++ b/src/afe.rs @@ -1,6 +1,7 @@ use embedded_hal; +use serde::{Serialize, Deserialize}; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub enum Gain { G1 = 0b00, G2 = 0b01, diff --git a/src/main.rs b/src/main.rs index 4401bfe..e909af7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,12 @@ use stm32h7xx_hal::{ prelude::*, stm32 as pac, }; +use pounder::Error; + +use heapless::{ + String, + consts::*, +}; use embedded_hal::{ digital::v2::OutputPin, @@ -111,12 +117,64 @@ type AFE2 = afe::ProgrammableGainAmplifier< hal::gpio::gpiod::PD14>, hal::gpio::gpiod::PD15>>; +macro_rules! route_request { + ($request:ident, $buffer:ident, + readable_attributes: [$(($read_attribute:tt, $getter:tt)),*], + modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => { + match $request { + server::Request::Read{attribute} => { + match attribute { + $( + &$read_attribute => { + let value = match $getter() { + Ok(data) => data, + Err(_) => return server::Response::error(attribute, + "Failed to set attribute"), + }; + + $buffer = match serde_json_core::to_string(&value) { + Ok(data) => data, + Err(_) => return server::Response::error(attribute, + "Failed to encode attribute value"), + }; + + server::Response::success(attribute, &$buffer) + }, + )* + _ => server::Response::error(attribute, "Unknown attribute") + } + }, + server::Request::Write{attribute, value} => { + match attribute { + $( + &$write_attribute => { + let new_value = match serde_json_core::from_str::<$TYPE>(value) { + Ok(data) => data, + Err(_) => return server::Response::error(attribute, + "Failed to decode value"), + }; + + match $setter(new_value) { + Ok(_) => server::Response::success(attribute, value), + Err(_) => server::Response::error(attribute, + "Failed to set attribute"), + } + } + )* + _ => server::Response::error(attribute, "Unknown attribute") + } + } + } + } +} + + #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { adc1: hal::spi::Spi, dac1: hal::spi::Spi, - _afe1: AFE1, + afe1: AFE1, adc2: hal::spi::Spi, dac2: hal::spi::Spi, @@ -446,7 +504,7 @@ const APP: () = { dac1: dac1_spi, adc2: adc2_spi, dac2: dac2_spi, - _afe1: afe1, + afe1: afe1, _afe2: afe2, dbg_pin: debug_pin, @@ -504,7 +562,7 @@ const APP: () = { cortex_m::asm::bkpt(); } - #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch])] + #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch, afe1])] fn idle(mut c: idle::Context) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); @@ -536,6 +594,8 @@ const APP: () = { // TODO: Replace with reference to CPU clock from CCDR. next_ms += 400_000.cycles(); + let buffer: String = String::new(); + loop { let tick = Instant::now() > next_ms; @@ -574,12 +634,16 @@ const APP: () = { .listen(1235) .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); } else { - server.poll(socket, |req: &server::Request| { - if req.channel < 2 { - c.resources.iir_ch.lock(|iir_ch| { - iir_ch[req.channel as usize] = req.iir - }); - } + server.poll(socket, |req| { + info!("Got request: {:?}", req); + route_request!(req, buffer, + readable_attributes: [ + ("stabilizer/afe0/gain", (|| Ok::(c.resources.afe1.get_gain()))) + ], + modifiable_attributes: [ + ("stabilizer/afe0/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe1.set_gain(gain)))) + ] + ) }); } } diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index b3ce19f..d1f445b 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -1,14 +1,14 @@ use mcp23017; use ad9959; -pub mod error; +mod error; pub mod attenuators; mod rf_power; pub mod types; use super::hal; -use error::Error; +pub use error::Error; use attenuators::AttenuatorInterface; use types::{DdsChannel, InputChannel}; use rf_power::PowerMeasurementInterface; diff --git a/src/server.rs b/src/server.rs index 8cd48ce..e2bf5e1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,7 +8,6 @@ use core::fmt::Write; use serde::{ - de::DeserializeOwned, Deserialize, Serialize }; @@ -19,18 +18,43 @@ use serde_json_core::{ }; use super::net; -use super::iir::IIR; -#[derive(Deserialize, Serialize)] -pub struct Request { - pub channel: u8, - pub iir: IIR, +#[derive(Deserialize, Serialize, Debug)] +pub enum Request<'a, 'b> { + Read{attribute: &'a str}, + Write{attribute: &'a str, value: &'b str}, } #[derive(Serialize)] -pub struct Response<'a> { +pub struct Response<'a, 'b> { code: i32, - message: &'a str, + attribute: &'a str, + value: &'b str, +} + +impl<'a, 'b> Response<'a, 'b> { + pub fn success<'c, 'd>(attribute: &'c str, value: &'d str) -> Self + where + 'c: 'a, + 'd: 'b, + { + Self { code: 200, attribute: attribute, value: value} + } + + pub fn error<'c, 'd>(attribute: &'c str, message: &'d str) -> Self + where + 'c: 'a, + 'd: 'b, + { + Self { code: 400, attribute: attribute, value: message} + } + + pub fn custom<'c>(code: i32, message : &'c str) -> Self + where + 'c: 'b, + { + Self { code: code, attribute: "", value: message} + } } #[derive(Serialize)] @@ -61,73 +85,54 @@ impl Server { } } - pub fn poll( + pub fn poll<'a, 'b, F>( &mut self, socket: &mut net::socket::TcpSocket, f: F, - ) -> Option + ) where - T: DeserializeOwned, - F: FnOnce(&T) -> R, + F: FnOnce(&Request) -> Response<'a, 'b> { while socket.can_recv() { - let found = socket - .recv(|buf| { - let (len, found) = - match buf.iter().position(|&c| c as char == '\n') { - Some(end) => (end + 1, true), - None => (buf.len(), false), - }; - if self.data.len() + len >= self.data.capacity() { - self.discard = true; - self.data.clear(); - } else if !self.discard && len > 0 { - self.data.extend_from_slice(&buf[..len]).unwrap(); - } - (len, found) - }) - .unwrap(); + let found = socket.recv(|buf| { + let (len, found) = + match buf.iter().position(|&c| c as char == '\n') { + Some(end) => (end + 1, true), + None => (buf.len(), false), + }; + if self.data.len() + len >= self.data.capacity() { + self.discard = true; + self.data.clear(); + } else if !self.discard && len > 0 { + self.data.extend_from_slice(&buf[..len]).unwrap(); + } + (len, found) + }).unwrap(); + if found { if self.discard { self.discard = false; - json_reply( - socket, - &Response { - code: 520, - message: "command buffer overflow", - }, + json_reply(socket, &Response::custom(520, "command buffer overflow"), ); self.data.clear(); } else { - let r = from_slice::(&self.data[..self.data.len() - 1]); + let r = from_slice::(&self.data[..self.data.len() - 1]); self.data.clear(); match r { Ok(res) => { - let r = f(&res); - json_reply( - socket, - &Response { - code: 200, - message: "ok", - }, - ); - return Some(r); - } + let response = f(&res); + json_reply(socket, &response); + return; + }, Err(err) => { warn!("parse error {:?}", err); - json_reply( - socket, - &Response { - code: 550, - message: "parse error", - }, + json_reply(socket, &Response::custom(550, "parse error"), ); } } } } } - None } }