Using custom branch of miniconf
This commit is contained in:
parent
a772ccc38a
commit
702ccc231d
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -344,6 +344,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_stringset"
|
name = "derive_stringset"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/vertigo-designs/miniconf.git?branch=rs/cleanup#396a759356ae977d718ef6d30cfa6481f0d40b2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -581,6 +582,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "miniconf"
|
name = "miniconf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/vertigo-designs/miniconf.git?branch=rs/cleanup#396a759356ae977d718ef6d30cfa6481f0d40b2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_stringset",
|
"derive_stringset",
|
||||||
"serde",
|
"serde",
|
||||||
@ -909,7 +911,6 @@ dependencies = [
|
|||||||
"panic-semihosting",
|
"panic-semihosting",
|
||||||
"paste",
|
"paste",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-json-core 0.1.0",
|
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
"stm32h7xx-hal",
|
"stm32h7xx-hal",
|
||||||
]
|
]
|
||||||
|
@ -36,7 +36,6 @@ panic-semihosting = { version = "0.5", optional = true }
|
|||||||
panic-halt = "0.2"
|
panic-halt = "0.2"
|
||||||
serde = { version = "1.0", features = ["derive"], default-features = false }
|
serde = { version = "1.0", features = ["derive"], default-features = false }
|
||||||
heapless = { version = "0.5", features = ["serde"] }
|
heapless = { version = "0.5", features = ["serde"] }
|
||||||
serde-json-core = "0.1"
|
|
||||||
cortex-m-rtic = "0.5.5"
|
cortex-m-rtic = "0.5.5"
|
||||||
embedded-hal = "0.2.4"
|
embedded-hal = "0.2.4"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -48,9 +47,8 @@ ad9959 = { path = "ad9959" }
|
|||||||
minimq = { git = "https://github.com/quartiq/minimq.git" }
|
minimq = { git = "https://github.com/quartiq/minimq.git" }
|
||||||
|
|
||||||
[dependencies.miniconf]
|
[dependencies.miniconf]
|
||||||
# git = "https://github.com/vertigo-designs/miniconf.git"
|
git = "https://github.com/vertigo-designs/miniconf.git"
|
||||||
# branch = "james/derive_stringset"
|
branch = "rs/cleanup"
|
||||||
path = "../../vertigo-designs/miniconf"
|
|
||||||
|
|
||||||
[dependencies.mcp23017]
|
[dependencies.mcp23017]
|
||||||
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
git = "https://github.com/mrd0ll4r/mcp23017.git"
|
||||||
|
@ -10,9 +10,8 @@ serde = { version = "1.0", features = ["derive"], default-features = false }
|
|||||||
serde-json-core = "0.1"
|
serde-json-core = "0.1"
|
||||||
|
|
||||||
[dependencies.miniconf]
|
[dependencies.miniconf]
|
||||||
# git = "https://github.com/vertigo-designs/miniconf.git"
|
git = "https://github.com/vertigo-designs/miniconf.git"
|
||||||
# branch = "james/derive_stringset"
|
branch = "rs/cleanup"
|
||||||
path = "../../../vertigo-designs/miniconf"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use super::{abs, copysign, macc, max, min};
|
use super::{abs, copysign, macc, max, min};
|
||||||
use core::f32;
|
use core::f32;
|
||||||
|
|
||||||
|
use miniconf::StringSet;
|
||||||
|
|
||||||
/// IIR state and coefficients type.
|
/// IIR state and coefficients type.
|
||||||
///
|
///
|
||||||
/// To represent the IIR state (input and output memory) during the filter update
|
/// To represent the IIR state (input and output memory) during the filter update
|
||||||
@ -38,7 +40,7 @@ pub type IIRState = [f32; 5];
|
|||||||
/// Therefore it can trivially implement bump-less transfer.
|
/// Therefore it can trivially implement bump-less transfer.
|
||||||
/// * Cascading multiple IIR filters allows stable and robust
|
/// * Cascading multiple IIR filters allows stable and robust
|
||||||
/// implementation of transfer functions beyond bequadratic terms.
|
/// implementation of transfer functions beyond bequadratic terms.
|
||||||
#[derive(Copy, Clone, Deserialize, Serialize)]
|
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, StringSet)]
|
||||||
pub struct IIR {
|
pub struct IIR {
|
||||||
pub ba: IIRState,
|
pub ba: IIRState,
|
||||||
pub y_offset: f32,
|
pub y_offset: f32,
|
||||||
|
@ -3,29 +3,36 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
|
||||||
|
|
||||||
use miniconf::StringSet;
|
|
||||||
|
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
use rtic::cyccnt::{Instant, U32Ext};
|
use rtic::cyccnt::{Instant, U32Ext};
|
||||||
|
|
||||||
use stabilizer::hardware;
|
use stabilizer::hardware;
|
||||||
|
|
||||||
|
use miniconf::StringSet;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use dsp::iir;
|
use dsp::iir;
|
||||||
use hardware::{Adc0Input, Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1, MqttAction};
|
use hardware::{Adc0Input, Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1, MqttAction};
|
||||||
|
|
||||||
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
||||||
|
|
||||||
const TCP_RX_BUFFER_SIZE: usize = 8192;
|
|
||||||
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
|
||||||
|
|
||||||
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
const IIR_CASCADE_LENGTH: usize = 1;
|
||||||
|
|
||||||
#[derive(Default, StringSet)]
|
#[derive(Debug, Deserialize, StringSet)]
|
||||||
struct Settings {
|
pub struct Settings {
|
||||||
pub afe_gain: [hardware::AfeGain; 2],
|
test: u32,
|
||||||
//iir: [[iir::IIR; IIR_CASCADE_LENGTH]; 2],
|
iir: [[iir::IIR; IIR_CASCADE_LENGTH]; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
test: 5,
|
||||||
|
iir: [[iir::IIR::default(); IIR_CASCADE_LENGTH]; 2],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
|
||||||
@ -58,7 +65,7 @@ const APP: () = {
|
|||||||
stabilizer.adc_dac_timer.start();
|
stabilizer.adc_dac_timer.start();
|
||||||
|
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
mqtt_interface: hardware::MqttInterface::new(stabilizer.net.stack, Settings::default()),
|
mqtt_interface: hardware::MqttInterface::new(stabilizer.net.stack, Settings::new()),
|
||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
adcs: stabilizer.adcs,
|
adcs: stabilizer.adcs,
|
||||||
dacs: stabilizer.dacs,
|
dacs: stabilizer.dacs,
|
||||||
@ -135,11 +142,9 @@ const APP: () = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])]
|
#[task(priority = 1, resources=[mqtt_interface, afes, iir_ch])]
|
||||||
fn settings_update(c: settings_update::Context) {
|
fn settings_update(mut c: settings_update::Context) {
|
||||||
let settings = c.resources.mqtt_interface.settings.borrow();
|
let settings = c.resources.mqtt_interface.settings.borrow();
|
||||||
//c.resources.iir_ch.lock(|iir_ch| *iir_ch = settings.iir);
|
c.resources.iir_ch.lock(|iir| *iir = settings.iir);
|
||||||
c.resources.afes.0.set_gain(settings.afe_gain[0]);
|
|
||||||
c.resources.afes.1.set_gain(settings.afe_gain[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[task(binds = ETH, priority = 1)]
|
#[task(binds = ETH, priority = 1)]
|
||||||
|
@ -5,15 +5,10 @@
|
|||||||
|
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
use rtic::cyccnt::{Instant, U32Ext};
|
use rtic::cyccnt::{Instant, U32Ext};
|
||||||
|
|
||||||
use heapless::{consts::*, String};
|
|
||||||
|
|
||||||
use stabilizer::{
|
use stabilizer::{
|
||||||
hardware, server, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2,
|
hardware, ADC_SAMPLE_TICKS_LOG2, SAMPLE_BUFFER_SIZE_LOG2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use dsp::{iir, iir_int, lockin::Lockin, reciprocal_pll::TimestampHandler};
|
use dsp::{iir, iir_int, lockin::Lockin, reciprocal_pll::TimestampHandler};
|
||||||
@ -23,9 +18,6 @@ use hardware::{
|
|||||||
|
|
||||||
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
||||||
|
|
||||||
const TCP_RX_BUFFER_SIZE: usize = 8192;
|
|
||||||
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
|
||||||
|
|
||||||
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
// The number of cascaded IIR biquads per channel. Select 1 or 2!
|
||||||
const IIR_CASCADE_LENGTH: usize = 1;
|
const IIR_CASCADE_LENGTH: usize = 1;
|
||||||
|
|
||||||
@ -35,7 +27,7 @@ const APP: () = {
|
|||||||
afes: (AFE0, AFE1),
|
afes: (AFE0, AFE1),
|
||||||
adcs: (Adc0Input, Adc1Input),
|
adcs: (Adc0Input, Adc1Input),
|
||||||
dacs: (Dac0Output, Dac1Output),
|
dacs: (Dac0Output, Dac1Output),
|
||||||
net_interface: hardware::Ethernet,
|
stack: hardware::NetworkStack,
|
||||||
|
|
||||||
// Format: iir_state[ch][cascade-no][coeff]
|
// Format: iir_state[ch][cascade-no][coeff]
|
||||||
#[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])]
|
#[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])]
|
||||||
@ -80,7 +72,7 @@ const APP: () = {
|
|||||||
afes: stabilizer.afes,
|
afes: stabilizer.afes,
|
||||||
adcs: stabilizer.adcs,
|
adcs: stabilizer.adcs,
|
||||||
dacs: stabilizer.dacs,
|
dacs: stabilizer.dacs,
|
||||||
net_interface: stabilizer.net.interface,
|
stack: stabilizer.net.stack,
|
||||||
timestamper: stabilizer.timestamper,
|
timestamper: stabilizer.timestamper,
|
||||||
|
|
||||||
pll,
|
pll,
|
||||||
@ -165,26 +157,8 @@ const APP: () = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(resources=[net_interface, iir_state, iir_ch, afes])]
|
#[idle(resources=[stack, iir_state, iir_ch, afes])]
|
||||||
fn idle(mut c: idle::Context) -> ! {
|
fn idle(c: idle::Context) -> ! {
|
||||||
let mut socket_set_entries: [_; 8] = Default::default();
|
|
||||||
let mut sockets =
|
|
||||||
smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]);
|
|
||||||
|
|
||||||
let mut rx_storage = [0; TCP_RX_BUFFER_SIZE];
|
|
||||||
let mut tx_storage = [0; TCP_TX_BUFFER_SIZE];
|
|
||||||
let tcp_handle = {
|
|
||||||
let tcp_rx_buffer =
|
|
||||||
smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
|
|
||||||
let tcp_tx_buffer =
|
|
||||||
smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]);
|
|
||||||
let tcp_socket =
|
|
||||||
smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
|
||||||
sockets.add(tcp_socket)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut server = server::Server::new();
|
|
||||||
|
|
||||||
let mut time = 0u32;
|
let mut time = 0u32;
|
||||||
let mut next_ms = Instant::now();
|
let mut next_ms = Instant::now();
|
||||||
|
|
||||||
@ -199,118 +173,7 @@ const APP: () = {
|
|||||||
time += 1;
|
time += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let sleep = c.resources.stack.update(time);
|
||||||
let socket =
|
|
||||||
&mut *sockets.get::<smoltcp::socket::TcpSocket>(tcp_handle);
|
|
||||||
if socket.state() == smoltcp::socket::TcpState::CloseWait {
|
|
||||||
socket.close();
|
|
||||||
} else if !(socket.is_open() || socket.is_listening()) {
|
|
||||||
socket
|
|
||||||
.listen(1235)
|
|
||||||
.unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
|
||||||
} else {
|
|
||||||
server.poll(socket, |req| {
|
|
||||||
info!("Got request: {:?}", req);
|
|
||||||
stabilizer::route_request!(req,
|
|
||||||
readable_attributes: [
|
|
||||||
"stabilizer/iir/state": (|| {
|
|
||||||
let state = c.resources.iir_state.lock(|iir_state|
|
|
||||||
server::Status {
|
|
||||||
t: time,
|
|
||||||
x0: iir_state[0][0][0],
|
|
||||||
y0: iir_state[0][0][2],
|
|
||||||
x1: iir_state[1][0][0],
|
|
||||||
y1: iir_state[1][0][2],
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok::<server::Status, ()>(state)
|
|
||||||
}),
|
|
||||||
// "_b" means cascades 2nd IIR
|
|
||||||
"stabilizer/iir_b/state": (|| { let state = c.resources.iir_state.lock(|iir_state|
|
|
||||||
server::Status {
|
|
||||||
t: time,
|
|
||||||
x0: iir_state[0][IIR_CASCADE_LENGTH-1][0],
|
|
||||||
y0: iir_state[0][IIR_CASCADE_LENGTH-1][2],
|
|
||||||
x1: iir_state[1][IIR_CASCADE_LENGTH-1][0],
|
|
||||||
y1: iir_state[1][IIR_CASCADE_LENGTH-1][2],
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok::<server::Status, ()>(state)
|
|
||||||
}),
|
|
||||||
"stabilizer/afe0/gain": (|| c.resources.afes.0.get_gain()),
|
|
||||||
"stabilizer/afe1/gain": (|| c.resources.afes.1.get_gain())
|
|
||||||
],
|
|
||||||
|
|
||||||
modifiable_attributes: [
|
|
||||||
"stabilizer/iir0/state": server::IirRequest, (|req: server::IirRequest| {
|
|
||||||
c.resources.iir_ch.lock(|iir_ch| {
|
|
||||||
if req.channel > 1 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
iir_ch[req.channel as usize][0] = req.iir;
|
|
||||||
|
|
||||||
Ok::<server::IirRequest, ()>(req)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
"stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| {
|
|
||||||
c.resources.iir_ch.lock(|iir_ch| {
|
|
||||||
if req.channel > 1 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
iir_ch[req.channel as usize][0] = req.iir;
|
|
||||||
|
|
||||||
Ok::<server::IirRequest, ()>(req)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
"stabilizer/iir_b0/state": server::IirRequest, (|req: server::IirRequest| {
|
|
||||||
c.resources.iir_ch.lock(|iir_ch| {
|
|
||||||
if req.channel > 1 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir;
|
|
||||||
|
|
||||||
Ok::<server::IirRequest, ()>(req)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
"stabilizer/iir_b1/state": server::IirRequest,(|req: server::IirRequest| {
|
|
||||||
c.resources.iir_ch.lock(|iir_ch| {
|
|
||||||
if req.channel > 1 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
iir_ch[req.channel as usize][IIR_CASCADE_LENGTH-1] = req.iir;
|
|
||||||
|
|
||||||
Ok::<server::IirRequest, ()>(req)
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
"stabilizer/afe0/gain": hardware::AfeGain, (|gain| {
|
|
||||||
c.resources.afes.0.set_gain(gain);
|
|
||||||
Ok::<(), ()>(())
|
|
||||||
}),
|
|
||||||
"stabilizer/afe1/gain": hardware::AfeGain, (|gain| {
|
|
||||||
c.resources.afes.1.set_gain(gain);
|
|
||||||
Ok::<(), ()>(())
|
|
||||||
})
|
|
||||||
]
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sleep = match c.resources.net_interface.poll(
|
|
||||||
&mut sockets,
|
|
||||||
smoltcp::time::Instant::from_millis(time as i64),
|
|
||||||
) {
|
|
||||||
Ok(changed) => !changed,
|
|
||||||
Err(smoltcp::Error::Unrecognized) => true,
|
|
||||||
Err(e) => {
|
|
||||||
info!("iface poll error: {:?}", e);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if sleep {
|
if sleep {
|
||||||
cortex_m::asm::wfi();
|
cortex_m::asm::wfi();
|
||||||
|
@ -50,7 +50,7 @@ where
|
|||||||
match self.client.borrow_mut().poll(|client, topic, message, properties| {
|
match self.client.borrow_mut().poll(|client, topic, message, properties| {
|
||||||
let mut split = topic.split('/');
|
let mut split = topic.split('/');
|
||||||
// TODO: Verify topic ID against our ID.
|
// TODO: Verify topic ID against our ID.
|
||||||
let id = split.next().unwrap();
|
let _id = split.next().unwrap();
|
||||||
|
|
||||||
// Process the command
|
// Process the command
|
||||||
let command = split.next().unwrap();
|
let command = split.next().unwrap();
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
pub mod hardware;
|
pub mod hardware;
|
||||||
pub mod server;
|
|
||||||
|
|
||||||
// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is
|
// The number of ticks in the ADC sampling timer. The timer runs at 100MHz, so the step size is
|
||||||
// equal to 10ns per tick.
|
// equal to 10ns per tick.
|
||||||
|
278
src/server.rs
278
src/server.rs
@ -1,278 +0,0 @@
|
|||||||
use core::fmt::Write;
|
|
||||||
use heapless::{consts::*, String, Vec};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json_core::{de::from_slice, ser::to_string};
|
|
||||||
use smoltcp as net;
|
|
||||||
|
|
||||||
use dsp::iir;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! route_request {
|
|
||||||
($request:ident,
|
|
||||||
readable_attributes: [$($read_attribute:tt: $getter:tt),*],
|
|
||||||
modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => {
|
|
||||||
match $request.req {
|
|
||||||
server::AccessRequest::Read => {
|
|
||||||
match $request.attribute {
|
|
||||||
$(
|
|
||||||
$read_attribute => {
|
|
||||||
#[allow(clippy::redundant_closure_call)]
|
|
||||||
let value = match $getter() {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => return server::Response::error($request.attribute,
|
|
||||||
"Failed to read attribute"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let encoded_data: String<U256> = match serde_json_core::to_string(&value) {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => return server::Response::error($request.attribute,
|
|
||||||
"Failed to encode attribute value"),
|
|
||||||
};
|
|
||||||
|
|
||||||
server::Response::success($request.attribute, &encoded_data)
|
|
||||||
},
|
|
||||||
)*
|
|
||||||
_ => server::Response::error($request.attribute, "Unknown attribute")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
server::AccessRequest::Write => {
|
|
||||||
match $request.attribute {
|
|
||||||
$(
|
|
||||||
$write_attribute => {
|
|
||||||
let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => return server::Response::error($request.attribute,
|
|
||||||
"Failed to decode value"),
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(clippy::redundant_closure_call)]
|
|
||||||
match $setter(new_value) {
|
|
||||||
Ok(_) => server::Response::success($request.attribute, &$request.value),
|
|
||||||
Err(_) => server::Response::error($request.attribute,
|
|
||||||
"Failed to set attribute"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
_ => server::Response::error($request.attribute, "Unknown attribute")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
pub enum AccessRequest {
|
|
||||||
Read,
|
|
||||||
Write,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
|
||||||
pub struct Request<'a> {
|
|
||||||
pub req: AccessRequest,
|
|
||||||
pub attribute: &'a str,
|
|
||||||
pub value: String<U256>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct IirRequest {
|
|
||||||
pub channel: u8,
|
|
||||||
pub iir: iir::IIR,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
pub struct Response {
|
|
||||||
code: i32,
|
|
||||||
attribute: String<U256>,
|
|
||||||
value: String<U256>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Request<'a> {
|
|
||||||
pub fn restore_value(&mut self) {
|
|
||||||
let mut new_value: String<U256> = String::new();
|
|
||||||
for byte in self.value.as_str().chars() {
|
|
||||||
if byte == '\'' {
|
|
||||||
new_value.push('"').unwrap();
|
|
||||||
} else {
|
|
||||||
new_value.push(byte).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
|
||||||
/// Remove all double quotation marks from the `value` field of a response.
|
|
||||||
fn sanitize_value(&mut self) {
|
|
||||||
let mut new_value: String<U256> = String::new();
|
|
||||||
for byte in self.value.as_str().chars() {
|
|
||||||
if byte == '"' {
|
|
||||||
new_value.push('\'').unwrap();
|
|
||||||
} else {
|
|
||||||
new_value.push(byte).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove all double quotation marks from the `value` field of a response and wrap it in single
|
|
||||||
/// quotes.
|
|
||||||
fn wrap_and_sanitize_value(&mut self) {
|
|
||||||
let mut new_value: String<U256> = String::new();
|
|
||||||
new_value.push('\'').unwrap();
|
|
||||||
for byte in self.value.as_str().chars() {
|
|
||||||
if byte == '"' {
|
|
||||||
new_value.push('\'').unwrap();
|
|
||||||
} else {
|
|
||||||
new_value.push(byte).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_value.push('\'').unwrap();
|
|
||||||
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a successful reply.
|
|
||||||
///
|
|
||||||
/// Note: `value` will be sanitized to convert all single quotes to double quotes.
|
|
||||||
///
|
|
||||||
/// Args:
|
|
||||||
/// * `attrbute` - The attribute of the success.
|
|
||||||
/// * `value` - The value of the attribute.
|
|
||||||
pub fn success(attribute: &str, value: &str) -> Self {
|
|
||||||
let mut res = Self {
|
|
||||||
code: 200,
|
|
||||||
attribute: String::from(attribute),
|
|
||||||
value: String::from(value),
|
|
||||||
};
|
|
||||||
res.sanitize_value();
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct an error reply.
|
|
||||||
///
|
|
||||||
/// Note: `message` will be sanitized to convert all single quotes to double quotes.
|
|
||||||
///
|
|
||||||
/// Args:
|
|
||||||
/// * `attrbute` - The attribute of the success.
|
|
||||||
/// * `message` - The message denoting the error.
|
|
||||||
pub fn error(attribute: &str, message: &str) -> Self {
|
|
||||||
let mut res = Self {
|
|
||||||
code: 400,
|
|
||||||
attribute: String::from(attribute),
|
|
||||||
value: String::from(message),
|
|
||||||
};
|
|
||||||
res.wrap_and_sanitize_value();
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a custom reply.
|
|
||||||
///
|
|
||||||
/// Note: `message` will be sanitized to convert all single quotes to double quotes.
|
|
||||||
///
|
|
||||||
/// Args:
|
|
||||||
/// * `attrbute` - The attribute of the success.
|
|
||||||
/// * `message` - The message denoting the status.
|
|
||||||
pub fn custom(code: i32, message: &str) -> Self {
|
|
||||||
let mut res = Self {
|
|
||||||
code,
|
|
||||||
attribute: String::from(""),
|
|
||||||
value: String::from(message),
|
|
||||||
};
|
|
||||||
res.wrap_and_sanitize_value();
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
pub struct Status {
|
|
||||||
pub t: u32,
|
|
||||||
pub x0: f32,
|
|
||||||
pub y0: f32,
|
|
||||||
pub x1: f32,
|
|
||||||
pub y1: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
|
||||||
let mut u: String<U512> = to_string(msg).unwrap();
|
|
||||||
u.push('\n').unwrap();
|
|
||||||
socket.write_str(&u).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Server {
|
|
||||||
data: Vec<u8, U256>,
|
|
||||||
discard: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Server {
|
|
||||||
/// Construct a new server object for managing requests.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
data: Vec::new(),
|
|
||||||
discard: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Poll the server for potential data updates.
|
|
||||||
///
|
|
||||||
/// Args:
|
|
||||||
/// * `socket` - The socket to check contents from.
|
|
||||||
/// * `f` - A closure that can be called if a request has been received on the server.
|
|
||||||
pub fn poll<F>(&mut self, socket: &mut net::socket::TcpSocket, mut f: F)
|
|
||||||
where
|
|
||||||
F: FnMut(&Request) -> Response,
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
if found {
|
|
||||||
if self.discard {
|
|
||||||
self.discard = false;
|
|
||||||
json_reply(
|
|
||||||
socket,
|
|
||||||
&Response::custom(520, "command buffer overflow"),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let r = from_slice::<Request>(
|
|
||||||
&self.data[..self.data.len() - 1],
|
|
||||||
);
|
|
||||||
match r {
|
|
||||||
Ok(mut res) => {
|
|
||||||
// Note that serde_json_core doesn't escape quotations within a string.
|
|
||||||
// To account for this, we manually translate all single quotes to
|
|
||||||
// double quotes. This occurs because we doubly-serialize this field in
|
|
||||||
// some cases.
|
|
||||||
res.restore_value();
|
|
||||||
let response = f(&res);
|
|
||||||
json_reply(socket, &response);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!("parse error {:?}", err);
|
|
||||||
json_reply(
|
|
||||||
socket,
|
|
||||||
&Response::custom(550, "parse error"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.data.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user