Using custom branch of miniconf

master
Ryan Summers 2021-01-27 18:15:35 +01:00
parent a772ccc38a
commit 702ccc231d
9 changed files with 35 additions and 446 deletions

3
Cargo.lock generated
View File

@ -344,6 +344,7 @@ dependencies = [
[[package]]
name = "derive_stringset"
version = "0.1.0"
source = "git+https://github.com/vertigo-designs/miniconf.git?branch=rs/cleanup#396a759356ae977d718ef6d30cfa6481f0d40b2f"
dependencies = [
"proc-macro2",
"quote",
@ -581,6 +582,7 @@ dependencies = [
[[package]]
name = "miniconf"
version = "0.1.0"
source = "git+https://github.com/vertigo-designs/miniconf.git?branch=rs/cleanup#396a759356ae977d718ef6d30cfa6481f0d40b2f"
dependencies = [
"derive_stringset",
"serde",
@ -909,7 +911,6 @@ dependencies = [
"panic-semihosting",
"paste",
"serde",
"serde-json-core 0.1.0",
"smoltcp",
"stm32h7xx-hal",
]

View File

@ -36,7 +36,6 @@ panic-semihosting = { version = "0.5", optional = true }
panic-halt = "0.2"
serde = { version = "1.0", features = ["derive"], default-features = false }
heapless = { version = "0.5", features = ["serde"] }
serde-json-core = "0.1"
cortex-m-rtic = "0.5.5"
embedded-hal = "0.2.4"
nb = "1.0.0"
@ -48,9 +47,8 @@ ad9959 = { path = "ad9959" }
minimq = { git = "https://github.com/quartiq/minimq.git" }
[dependencies.miniconf]
# git = "https://github.com/vertigo-designs/miniconf.git"
# branch = "james/derive_stringset"
path = "../../vertigo-designs/miniconf"
git = "https://github.com/vertigo-designs/miniconf.git"
branch = "rs/cleanup"
[dependencies.mcp23017]
git = "https://github.com/mrd0ll4r/mcp23017.git"

View File

@ -10,9 +10,8 @@ serde = { version = "1.0", features = ["derive"], default-features = false }
serde-json-core = "0.1"
[dependencies.miniconf]
# git = "https://github.com/vertigo-designs/miniconf.git"
# branch = "james/derive_stringset"
path = "../../../vertigo-designs/miniconf"
git = "https://github.com/vertigo-designs/miniconf.git"
branch = "rs/cleanup"
[dev-dependencies]
criterion = "0.3"

View File

@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
use super::{abs, copysign, macc, max, min};
use core::f32;
use miniconf::StringSet;
/// IIR state and coefficients type.
///
/// 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.
/// * Cascading multiple IIR filters allows stable and robust
/// implementation of transfer functions beyond bequadratic terms.
#[derive(Copy, Clone, Deserialize, Serialize)]
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, StringSet)]
pub struct IIR {
pub ba: IIRState,
pub y_offset: f32,

View File

@ -3,29 +3,36 @@
#![no_main]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
use miniconf::StringSet;
use stm32h7xx_hal as hal;
use rtic::cyccnt::{Instant, U32Ext};
use stabilizer::hardware;
use miniconf::StringSet;
use serde::Deserialize;
use dsp::iir;
use hardware::{Adc0Input, Adc1Input, Dac0Output, Dac1Output, AFE0, AFE1, MqttAction};
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!
const IIR_CASCADE_LENGTH: usize = 1;
#[derive(Default, StringSet)]
struct Settings {
pub afe_gain: [hardware::AfeGain; 2],
//iir: [[iir::IIR; IIR_CASCADE_LENGTH]; 2],
#[derive(Debug, Deserialize, StringSet)]
pub struct Settings {
test: u32,
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)]
@ -58,7 +65,7 @@ const APP: () = {
stabilizer.adc_dac_timer.start();
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,
adcs: stabilizer.adcs,
dacs: stabilizer.dacs,
@ -135,11 +142,9 @@ const APP: () = {
}
#[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();
//c.resources.iir_ch.lock(|iir_ch| *iir_ch = settings.iir);
c.resources.afes.0.set_gain(settings.afe_gain[0]);
c.resources.afes.1.set_gain(settings.afe_gain[1]);
c.resources.iir_ch.lock(|iir| *iir = settings.iir);
}
#[task(binds = ETH, priority = 1)]

View File

@ -5,15 +5,10 @@
use stm32h7xx_hal as hal;
#[macro_use]
extern crate log;
use rtic::cyccnt::{Instant, U32Ext};
use heapless::{consts::*, String};
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};
@ -23,9 +18,6 @@ use hardware::{
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!
const IIR_CASCADE_LENGTH: usize = 1;
@ -35,7 +27,7 @@ const APP: () = {
afes: (AFE0, AFE1),
adcs: (Adc0Input, Adc1Input),
dacs: (Dac0Output, Dac1Output),
net_interface: hardware::Ethernet,
stack: hardware::NetworkStack,
// Format: iir_state[ch][cascade-no][coeff]
#[init([[[0.; 5]; IIR_CASCADE_LENGTH]; 2])]
@ -80,7 +72,7 @@ const APP: () = {
afes: stabilizer.afes,
adcs: stabilizer.adcs,
dacs: stabilizer.dacs,
net_interface: stabilizer.net.interface,
stack: stabilizer.net.stack,
timestamper: stabilizer.timestamper,
pll,
@ -165,26 +157,8 @@ const APP: () = {
}
}
#[idle(resources=[net_interface, iir_state, iir_ch, afes])]
fn idle(mut 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();
#[idle(resources=[stack, iir_state, iir_ch, afes])]
fn idle(c: idle::Context) -> ! {
let mut time = 0u32;
let mut next_ms = Instant::now();
@ -199,118 +173,7 @@ const APP: () = {
time += 1;
}
{
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
}
};
let sleep = c.resources.stack.update(time);
if sleep {
cortex_m::asm::wfi();

View File

@ -50,7 +50,7 @@ where
match self.client.borrow_mut().poll(|client, topic, message, properties| {
let mut split = topic.split('/');
// TODO: Verify topic ID against our ID.
let id = split.next().unwrap();
let _id = split.next().unwrap();
// Process the command
let command = split.next().unwrap();

View File

@ -4,7 +4,6 @@
extern crate log;
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
// equal to 10ns per tick.

View File

@ -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();
}
}
}
}