merge the network code

This commit is contained in:
Astro 2020-03-13 23:39:22 +01:00
parent 09dbb7d495
commit 69a4f5a5d2
7 changed files with 1012 additions and 86 deletions

34
Cargo.lock generated
View File

@ -148,6 +148,16 @@ version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "857afb5ee9e767c3a73b2ad7212b6deea0c3761a27db1e20ea0ed57ee352cfef" checksum = "857afb5ee9e767c3a73b2ad7212b6deea0c3761a27db1e20ea0ed57ee352cfef"
[[package]]
name = "lexical-core"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
dependencies = [
"bitflags",
"cfg-if",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.8" version = "0.4.8"
@ -163,12 +173,28 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]] [[package]]
name = "nb" name = "nb"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
[[package]]
name = "nom"
version = "5.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
dependencies = [
"memchr",
"version_check",
]
[[package]] [[package]]
name = "panic-abort" name = "panic-abort"
version = "0.3.2" version = "0.3.2"
@ -312,7 +338,9 @@ dependencies = [
"cortex-m-rt", "cortex-m-rt",
"embedded-hal", "embedded-hal",
"hash2hwaddr", "hash2hwaddr",
"lexical-core",
"log", "log",
"nom",
"panic-abort", "panic-abort",
"panic-semihosting", "panic-semihosting",
"smoltcp", "smoltcp",
@ -338,6 +366,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]] [[package]]
name = "void" name = "void"
version = "1.0.2" version = "1.0.2"

View File

@ -29,6 +29,8 @@ smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4
hash2hwaddr = { version = "0.0", optional = true } hash2hwaddr = { version = "0.0", optional = true }
bit_field = "0.10" bit_field = "0.10"
byteorder = { version = "1", default-features = false } byteorder = { version = "1", default-features = false }
nom = { version = "5", default-features = false }
lexical-core = { version = "0.7", default-features = false }
[features] [features]
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]

533
src/command_parser.rs Normal file
View File

@ -0,0 +1,533 @@
use core::fmt;
use nom::{
IResult,
branch::alt,
bytes::complete::{is_a, tag, take_while1},
character::{is_digit, complete::{char, one_of}},
combinator::{complete, map, opt, value},
sequence::{preceded, separated_pair},
multi::{fold_many0, fold_many1},
error::ErrorKind,
};
use lexical_core as lexical;
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
Parser(ErrorKind),
Incomplete,
UnexpectedInput(u8),
ParseNumber(lexical::Error)
}
impl<'t> From<nom::Err<(&'t [u8], ErrorKind)>> for Error {
fn from(e: nom::Err<(&'t [u8], ErrorKind)>) -> Self {
match e {
nom::Err::Incomplete(_) =>
Error::Incomplete,
nom::Err::Error((_, e)) =>
Error::Parser(e),
nom::Err::Failure((_, e)) =>
Error::Parser(e),
}
}
}
impl From<lexical::Error> for Error {
fn from(e: lexical::Error) -> Self {
Error::ParseNumber(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::Incomplete =>
"incomplete input".fmt(fmt),
Error::UnexpectedInput(c) => {
"unexpected input: ".fmt(fmt)?;
c.fmt(fmt)
}
Error::Parser(e) => {
"parser: ".fmt(fmt)?;
(e as &dyn core::fmt::Debug).fmt(fmt)
}
Error::ParseNumber(e) => {
"parsing number: ".fmt(fmt)?;
(e as &dyn core::fmt::Debug).fmt(fmt)
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ShowCommand {
Input,
Reporting,
Pwm,
Pid,
SteinhartHart,
PostFilter,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PidParameter {
Target,
KP,
KI,
KD,
OutputMin,
OutputMax,
IntegralMin,
IntegralMax,
}
/// Steinhart-Hart equation parameter
#[derive(Debug, Clone, PartialEq)]
pub enum ShParameter {
A,
B,
C,
ParallelR,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PwmConfig {
pub width: u16,
pub total: u16,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PwmMode {
Manual(PwmConfig),
Pid,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PwmSetup {
ISet(PwmMode),
MaxIPos(PwmConfig),
MaxINeg(PwmConfig),
MaxV(PwmConfig),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Command {
Quit,
Show(ShowCommand),
Reporting(bool),
Pwm {
channel: usize,
setup: PwmSetup,
},
Pid {
channel: usize,
parameter: PidParameter,
value: f32,
},
SteinhartHart {
channel: usize,
parameter: ShParameter,
value: f32,
},
PostFilter {
channel: usize,
rate: f32,
},
}
fn end(input: &[u8]) -> IResult<&[u8], ()> {
complete(
fold_many0(
one_of("\r\n\t "),
(), |(), _| ()
)
)(input)
}
fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
fold_many1(char(' '), (), |(), _| ())(input)
}
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u16, Error>> {
take_while1(is_digit)(input)
.map(|(input, digits)| {
let result = lexical::parse(digits)
.map_err(|e| e.into());
(input, result)
})
}
fn float(input: &[u8]) -> IResult<&[u8], Result<f32, Error>> {
let (input, sign) = opt(is_a("-"))(input)?;
let negative = sign.is_some();
let (input, digits) = take_while1(|c| is_digit(c) || c == '.' as u8)(input)?;
let result = lexical::parse(digits)
.map(|result: f32| if negative { -result } else { result })
.map_err(|e| e.into());
Ok((input, result))
}
fn off_on(input: &[u8]) -> IResult<&[u8], bool> {
alt((value(false, tag("off")),
value(true, tag("on"))
))(input)
}
fn channel(input: &[u8]) -> IResult<&[u8], usize> {
map(one_of("01"), |c| (c as usize) - ('0' as usize))(input)
}
fn report(input: &[u8]) -> IResult<&[u8], Command> {
preceded(
tag("report"),
alt((
preceded(
whitespace,
preceded(
tag("mode"),
alt((
preceded(
whitespace,
// `report mode <on | off>` - Switch repoting mode
map(off_on, Command::Reporting)
),
// `report mode` - Show current reporting state
value(Command::Show(ShowCommand::Reporting), end)
))
)),
// `report` - Report once
value(Command::Show(ShowCommand::Input), end)
))
)(input)
}
/// `pwm ... <width> <total>` - Set pwm duty cycle
fn pwm_config(input: &[u8]) -> IResult<&[u8], Result<PwmConfig, Error>> {
let (input, width) = unsigned(input)?;
let width = match width {
Ok(width) => width,
Err(e) => return Ok((input, Err(e.into()))),
};
let (input, _) = whitespace(input)?;
let (input, total) = unsigned(input)?;
let total = match total {
Ok(total) => total,
Err(e) => return Ok((input, Err(e.into()))),
};
Ok((input, Ok(PwmConfig { width, total })))
}
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<PwmSetup, Error>> {
alt((
map(
preceded(
tag("max_i_pos"),
preceded(
whitespace,
pwm_config
)
),
|result| result.map(PwmSetup::MaxIPos)
),
map(
preceded(
tag("max_i_neg"),
preceded(
whitespace,
pwm_config
)
),
|result| result.map(PwmSetup::MaxINeg)
),
map(
preceded(
tag("max_v"),
preceded(
whitespace,
pwm_config
)
),
|result| result.map(PwmSetup::MaxV)
),
map(pwm_config, |result| result.map(|config| {
PwmSetup::ISet(PwmMode::Manual(config))
}))
))(input)
}
/// `pwm <0-1> pid` - Set PWM to be controlled by PID
fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result<PwmSetup, Error>> {
value(Ok(PwmSetup::ISet(PwmMode::Pid)), tag("pid"))(input)
}
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("pwm")(input)?;
alt((
preceded(
whitespace,
map(
separated_pair(
channel,
whitespace,
alt((
pwm_pid,
pwm_setup
))
),
|(channel, setup)| setup.map(|setup| Command::Pwm { channel, setup })
)
),
value(Ok(Command::Show(ShowCommand::Pwm)), end)
))(input)
}
/// `pid <0-1> <parameter> <value>`
fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, channel) = channel(input)?;
let (input, _) = whitespace(input)?;
let (input, parameter) =
alt((value(PidParameter::Target, tag("target")),
value(PidParameter::KP, tag("kp")),
value(PidParameter::KI, tag("ki")),
value(PidParameter::KD, tag("kd")),
value(PidParameter::OutputMin, tag("output_min")),
value(PidParameter::OutputMax, tag("output_max")),
value(PidParameter::IntegralMin, tag("integral_min")),
value(PidParameter::IntegralMax, tag("integral_max"))
))(input)?;
let (input, _) = whitespace(input)?;
let (input, value) = float(input)?;
let result = value
.map(|value| Command::Pid { channel, parameter, value });
Ok((input, result))
}
/// `pid` | `pid <pid_parameter>`
fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("pid")(input)?;
alt((
preceded(
whitespace,
pid_parameter
),
value(Ok(Command::Show(ShowCommand::Pid)), end)
))(input)
}
/// `s-h <0-1> <parameter> <value>`
fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, channel) = channel(input)?;
let (input, _) = whitespace(input)?;
let (input, parameter) =
alt((value(ShParameter::A, tag("a")),
value(ShParameter::B, tag("b")),
value(ShParameter::C, tag("c")),
value(ShParameter::ParallelR, tag("parallel_r"))
))(input)?;
let (input, _) = whitespace(input)?;
let (input, value) = float(input)?;
let result = value
.map(|value| Command::SteinhartHart { channel, parameter, value });
Ok((input, result))
}
/// `s-h` | `s-h <steinhart_hart_parameter>`
fn steinhart_hart(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("s-h")(input)?;
alt((
preceded(
whitespace,
steinhart_hart_parameter
),
value(Ok(Command::Show(ShowCommand::SteinhartHart)), end)
))(input)
}
fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("postfilter")(input)?;
alt((
preceded(
whitespace,
|input| {
let (input, channel) = channel(input)?;
let (input, _) = whitespace(input)?;
let (input, _) = tag("rate")(input)?;
let (input, _) = whitespace(input)?;
let (input, rate) = float(input)?;
let result = rate
.map(|rate| Command::PostFilter {
channel, rate,
});
Ok((input, result))
}
),
value(Ok(Command::Show(ShowCommand::PostFilter)), end)
))(input)
}
fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
alt((value(Ok(Command::Quit), tag("quit")),
map(report, Ok),
pwm,
pid,
steinhart_hart,
postfilter,
))(input)
}
impl Command {
pub fn parse(input: &[u8]) -> Result<Self, Error> {
match command(input) {
Ok((b"", result)) =>
result,
Ok((input_remain, _)) =>
Err(Error::UnexpectedInput(input_remain[0])),
Err(e) =>
Err(e.into()),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn parse_quit() {
let command = Command::parse(b"quit");
assert_eq!(command, Ok(Command::Quit));
}
#[test]
fn parse_report() {
let command = Command::parse(b"report");
assert_eq!(command, Ok(Command::Show(ShowCommand::Input)));
}
#[test]
fn parse_report_mode() {
let command = Command::parse(b"report mode");
assert_eq!(command, Ok(Command::Show(ShowCommand::Reporting)));
}
#[test]
fn parse_report_mode_on() {
let command = Command::parse(b"report mode on");
assert_eq!(command, Ok(Command::Reporting(true)));
}
#[test]
fn parse_report_mode_off() {
let command = Command::parse(b"report mode off");
assert_eq!(command, Ok(Command::Reporting(false)));
}
#[test]
fn parse_pwm_manual() {
let command = Command::parse(b"pwm 1 16383 65535");
assert_eq!(command, Ok(Command::Pwm {
channel: 1,
setup: PwmSetup::ISet(PwmMode::Manual(PwmConfig {
width: 16383,
total: 65535,
})),
}));
}
#[test]
fn parse_pwm_pid() {
let command = Command::parse(b"pwm 0 pid");
assert_eq!(command, Ok(Command::Pwm {
channel: 0,
setup: PwmSetup::ISet(PwmMode::Pid),
}));
}
#[test]
fn parse_pwm_max_i_pos() {
let command = Command::parse(b"pwm 0 max_i_pos 7 13");
assert_eq!(command, Ok(Command::Pwm {
channel: 0,
setup: PwmSetup::MaxIPos(PwmConfig {
width: 7,
total: 13,
}),
}));
}
#[test]
fn parse_pwm_max_i_neg() {
let command = Command::parse(b"pwm 0 max_i_neg 128 65535");
assert_eq!(command, Ok(Command::Pwm {
channel: 0,
setup: PwmSetup::MaxINeg(PwmConfig {
width: 128,
total: 65535,
}),
}));
}
#[test]
fn parse_pwm_max_v() {
let command = Command::parse(b"pwm 0 max_v 32768 65535");
assert_eq!(command, Ok(Command::Pwm {
channel: 0,
setup: PwmSetup::MaxV(PwmConfig {
width: 32768,
total: 65535,
}),
}));
}
#[test]
fn parse_pid() {
let command = Command::parse(b"pid");
assert_eq!(command, Ok(Command::Show(ShowCommand::Pid)));
}
#[test]
fn parse_pid_target() {
let command = Command::parse(b"pid 0 target 36.5");
assert_eq!(command, Ok(Command::Pid {
channel: 0,
parameter: PidParameter::Target,
value: 36.5,
}));
}
#[test]
fn parse_pid_integral_max() {
let command = Command::parse(b"pid 1 integral_max 2000");
assert_eq!(command, Ok(Command::Pid {
channel: 1,
parameter: PidParameter::IntegralMax,
value: 2000.0,
}));
}
#[test]
fn parse_steinhart_hart() {
let command = Command::parse(b"s-h");
assert_eq!(command, Ok(Command::Show(ShowCommand::SteinhartHart)));
}
#[test]
fn parse_steinhart_hart_parallel_r() {
let command = Command::parse(b"s-h 1 parallel_r 23.05");
assert_eq!(command, Ok(Command::SteinhartHart {
channel: 1,
parameter: ShParameter::ParallelR,
value: 23.05,
}));
}
#[test]
fn parse_postfilter_rate() {
let command = Command::parse(b"postfilter 0 rate 21");
assert_eq!(command, Ok(Command::PostFilter {
channel: 0,
rate: 21.0,
}));
}
}

View File

@ -33,13 +33,28 @@ mod ad5680;
mod net; mod net;
mod server; mod server;
use server::Server; use server::Server;
mod session;
use session::{CHANNELS, Session, SessionOutput};
mod command_parser;
use command_parser::{Command, ShowCommand, PwmSetup, PwmMode};
mod timer; mod timer;
/// Interval at which to sample the ADC input and broadcast to all
/// clients. #[derive(Clone, Copy, Debug)]
/// struct ChannelState {
/// This should be a multiple of the `TIMER_RATE`. adc_data: Option<i32>,
const OUTPUT_INTERVAL: u32 = 1000; adc_time: Instant,
}
impl Default for ChannelState {
fn default() -> Self {
ChannelState {
adc_data: None,
adc_time: Instant::from_secs(0),
}
}
}
#[cfg(not(feature = "semihosting"))] #[cfg(not(feature = "semihosting"))]
const WATCHDOG_INTERVAL: u32 = 100; const WATCHDOG_INTERVAL: u32 = 100;
@ -49,15 +64,16 @@ const WATCHDOG_INTERVAL: u32 = 10_000;
#[cfg(not(feature = "generate-hwaddr"))] #[cfg(not(feature = "generate-hwaddr"))]
const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
const TCP_PORT: u16 = 23;
const HSE: MegaHertz = MegaHertz(8); const HSE: MegaHertz = MegaHertz(8);
/// Initialization and main loop /// Initialization and main loop
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
init_log(); init_log();
info!("Thermostat firmware"); info!("tecpak");
let mut cp = CorePeripherals::take().unwrap(); let mut cp = CorePeripherals::take().unwrap();
cp.SCB.enable_icache(); cp.SCB.enable_icache();
@ -102,10 +118,10 @@ fn main() -> ! {
}; };
info!("Net hwaddr: {}", hwaddr); info!("Net hwaddr: {}", hwaddr);
info!("Net startup"); let mut channel_states = [ChannelState::default(); CHANNELS];
net::run(dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| { net::run(dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| {
Server::run(iface, |server| { Server::<Session>::run(iface, |server| {
let mut last_output = 0_u32;
loop { loop {
let now = timer::now().0; let now = timer::now().0;
let instant = Instant::from_millis(i64::from(now)); let instant = Instant::from_millis(i64::from(now));
@ -115,17 +131,224 @@ fn main() -> ! {
warn!("poll: {:?}", e); warn!("poll: {:?}", e);
}); });
let now = timer::now().0; // ADC input
if now - last_output >= OUTPUT_INTERVAL { adc.data_ready().unwrap().map(|channel| {
// let adc_value = adc_input.read();
writeln!(server, "t={},pa3={}\r", now, 0.0 /*adc_value*/).unwrap();
last_output = now;
}
if let Some(channel) = adc.data_ready().unwrap() {
let data = adc.read_data().unwrap(); let data = adc.read_data().unwrap();
info!("ADC {}: {:08X}", channel, data);
let state = &mut channel_states[usize::from(channel)];
state.adc_data = Some(data);
state.adc_time = instant;
server.for_each(|_, session| session.set_report_pending(channel.into()));
});
// TCP protocol handling
server.for_each(|mut socket, session| {
if ! socket.is_open() {
let _ = socket.listen(TCP_PORT);
session.reset();
} else if socket.can_send() && socket.can_recv() && socket.send_capacity() - socket.send_queue() > 128 {
match socket.recv(|buf| session.feed(buf)) {
Ok(SessionOutput::Nothing) => {}
Ok(SessionOutput::Command(command)) => match command {
Command::Quit =>
socket.close(),
Command::Reporting(reporting) => {
let _ = writeln!(socket, "report={}", if reporting { "on" } else { "off" });
} }
Command::Show(ShowCommand::Reporting) => {
let _ = writeln!(socket, "report={}", if session.reporting() { "on" } else { "off" });
}
Command::Show(ShowCommand::Input) => {
for (channel, state) in channel_states.iter().enumerate() {
if let Some(adc_data) = state.adc_data {
let _ = writeln!(
socket, "t={} raw{}=0x{:06X}",
state.adc_time, channel, adc_data
);
// TODO: show pwm status et al
}
}
}
Command::Show(ShowCommand::Pid) => {
// for (channel, state) in states.iter().enumerate() {
// let _ = writeln!(socket, "PID settings for channel {}", channel);
// let pid = &state.pid;
// let _ = writeln!(socket, "- target={:.4}", pid.get_target());
// let p = pid.get_parameters();
// macro_rules! out {
// ($p: tt) => {
// let _ = writeln!(socket, "- {}={:.4}", stringify!($p), p.$p);
// };
// }
// out!(kp);
// out!(ki);
// out!(kd);
// out!(output_min);
// out!(output_max);
// out!(integral_min);
// out!(integral_max);
// let _ = writeln!(socket, "");
// }
}
Command::Show(ShowCommand::Pwm) => {
// for (channel, state) in states.iter().enumerate() {
// let _ = writeln!(
// socket, "channel {}: PID={}",
// channel,
// if state.pid_enabled { "engaged" } else { "disengaged" }
// );
// for pin in TecPin::VALID_VALUES {
// let (width, total) = match channel {
// 0 => tec0.get(*pin),
// 1 => tec1.get(*pin),
// _ => unreachable!(),
// };
// let _ = writeln!(socket, "- {}={}/{}", pin, width, total);
// }
// let _ = writeln!(socket, "");
// }
}
Command::Show(ShowCommand::SteinhartHart) => {
// for (channel, state) in states.iter().enumerate() {
// let _ = writeln!(
// socket, "channel {}: Steinhart-Hart equation parameters",
// channel,
// );
// let _ = writeln!(socket, "- a={}", state.sh.a);
// let _ = writeln!(socket, "- b={}", state.sh.b);
// let _ = writeln!(socket, "- c={}", state.sh.c);
// let _ = writeln!(socket, "- parallel_r={}", state.sh.parallel_r);
// let _ = writeln!(socket, "");
// }
}
Command::Show(ShowCommand::PostFilter) => {
// for (channel, _) in states.iter().enumerate() {
// match adc.get_postfilter(channel as u8).unwrap() {
// Some(filter) => {
// let _ = writeln!(
// socket, "channel {}: postfilter={:.2} SPS",
// channel, filter.output_rate().unwrap()
// );
// }
// None => {
// let _ = writeln!(
// socket, "channel {}: no postfilter",
// channel
// );
// }
// }
// }
}
Command::Pwm { channel, setup: PwmSetup::ISet(PwmMode::Pid) } => {
// states[channel].pid_enabled = true;
// let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
// );
}
Command::Pwm { channel, setup: PwmSetup::ISet(PwmMode::Manual(config))} => {
// states[channel].pid_enabled = false;
// let PwmConfig { width, total } = config;
// match channel {
// 0 => tec0.set(TecPin::ISet, width, total),
// 1 => tec1.set(TecPin::ISet, width, total),
// _ => unreachable!(),
// }
// let _ = writeln!(
// socket, "channel {}: PWM duty cycle manually set to {}/{}",
// channel, config.width, config.total
// );
}
Command::Pwm { channel, setup } => {
// let (pin, config) = match setup {
// PwmSetup::ISet(_) =>
// // Handled above
// unreachable!(),
// PwmSetup::MaxIPos(config) =>
// (TecPin::MaxIPos, config),
// PwmSetup::MaxINeg(config) =>
// (TecPin::MaxINeg, config),
// PwmSetup::MaxV(config) =>
// (TecPin::MaxV, config),
// };
// let PwmConfig { width, total } = config;
// match channel {
// 0 => tec0.set(pin, width, total),
// 1 => tec1.set(pin, width, total),
// _ => unreachable!(),
// }
// let _ = writeln!(
// socket, "channel {}: PWM {} reconfigured to {}/{}",
// channel, pin, width, total
// );
}
Command::Pid { channel, parameter, value } => {
// let pid = &mut states[channel].pid;
// use command_parser::PidParameter::*;
// match parameter {
// Target =>
// pid.set_target(value),
// KP =>
// pid.update_parameters(|parameters| parameters.kp = value),
// KI =>
// pid.update_parameters(|parameters| parameters.ki = value),
// KD =>
// pid.update_parameters(|parameters| parameters.kd = value),
// OutputMin =>
// pid.update_parameters(|parameters| parameters.output_min = value),
// OutputMax =>
// pid.update_parameters(|parameters| parameters.output_max = value),
// IntegralMin =>
// pid.update_parameters(|parameters| parameters.integral_min = value),
// IntegralMax =>
// pid.update_parameters(|parameters| parameters.integral_max = value),
// }
// pid.reset();
// let _ = writeln!(socket, "PID parameter updated");
}
Command::SteinhartHart { channel, parameter, value } => {
// let sh = &mut states[channel].sh;
// use command_parser::ShParameter::*;
// match parameter {
// A => sh.a = value,
// B => sh.b = value,
// C => sh.c = value,
// ParallelR => sh.parallel_r = value,
// }
// let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
}
Command::PostFilter { channel, rate } => {
// let filter = ad7172::PostFilter::closest(rate);
// match filter {
// Some(filter) => {
// adc.set_postfilter(channel as u8, Some(filter)).unwrap();
// let _ = writeln!(
// socket, "channel {}: postfilter set to {:.2} SPS",
// channel, filter.output_rate().unwrap()
// );
// }
// None => {
// let _ = writeln!(socket, "Unable to choose postfilter");
// }
// }
}
}
Ok(SessionOutput::Error(e)) => {
let _ = writeln!(socket, "Command error: {:?}", e);
}
Err(_) =>
socket.close(),
}
} else if socket.can_send() && socket.send_capacity() - socket.send_queue() > 256 {
while let Some(channel) = session.is_report_pending() {
let state = &mut channel_states[usize::from(channel)];
let _ = writeln!(
socket, "t={} raw{}=0x{:04X}",
state.adc_time, channel, state.adc_data.unwrap_or(0)
).map(|_| {
session.mark_report_sent(channel);
});
}
}
});
// Update watchdog // Update watchdog
wd.feed(); wd.feed();

View File

@ -43,7 +43,7 @@ pub fn run<F>(
eth_dev.enable_interrupt(); eth_dev.enable_interrupt();
// IP stack // IP stack
let local_addr = IpAddress::v4(192, 168, 69, 3); let local_addr = IpAddress::v4(192, 168, 1, 26);
let mut ip_addrs = [IpCidr::new(local_addr, 24)]; let mut ip_addrs = [IpCidr::new(local_addr, 24)];
let mut neighbor_storage = [None; 16]; let mut neighbor_storage = [None; 16];
let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]); let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]);

View File

@ -2,12 +2,16 @@ use core::fmt;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use smoltcp::{ use smoltcp::{
iface::EthernetInterface, iface::EthernetInterface,
socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer}, socket::{SocketSet, SocketHandle, TcpSocket, TcpSocketBuffer, SocketRef},
time::Instant, time::Instant,
}; };
const TCP_PORT: u16 = 23; pub struct SocketState<S> {
handle: SocketHandle,
state: S,
}
/// Number of server sockets and therefore concurrent client /// Number of server sockets and therefore concurrent client
/// sessions. Many data structures in `Server::run()` correspond to /// sessions. Many data structures in `Server::run()` correspond to
/// this const. /// this const.
@ -16,7 +20,25 @@ const SOCKET_COUNT: usize = 8;
const TCP_RX_BUFFER_SIZE: usize = 2048; const TCP_RX_BUFFER_SIZE: usize = 2048;
const TCP_TX_BUFFER_SIZE: usize = 2048; const TCP_TX_BUFFER_SIZE: usize = 2048;
macro_rules! create_socket { /// Contains a number of server sockets that get all sent the same
/// data (through `fmt::Write`).
pub struct Server<'a, 'b, S> {
net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>,
sockets: SocketSet<'b, 'b, 'b>,
states: [SocketState<S>; SOCKET_COUNT],
}
impl<'a, 'b, S: Default> Server<'a, 'b, S> {
/// Run a server with stack-allocated sockets
pub fn run<F>(net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>, f: F)
where
F: FnOnce(&mut Server<'a, '_, S>),
{
let mut sockets_storage: [_; SOCKET_COUNT] = Default::default();
let mut sockets = SocketSet::new(&mut sockets_storage[..]);
let mut states: [SocketState<S>; SOCKET_COUNT] = unsafe { MaybeUninit::uninit().assume_init() };
macro_rules! create_socket {
($set:ident, $rx_storage:ident, $tx_storage:ident, $target:expr) => { ($set:ident, $rx_storage:ident, $tx_storage:ident, $target:expr) => {
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE]; let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE]; let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
@ -25,36 +47,22 @@ macro_rules! create_socket {
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
$target = $set.add(tcp_socket); $target = $set.add(tcp_socket);
} }
} }
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, states[0].handle);
create_socket!(sockets, tcp_rx_storage1, tcp_tx_storage1, states[1].handle);
create_socket!(sockets, tcp_rx_storage2, tcp_tx_storage2, states[2].handle);
create_socket!(sockets, tcp_rx_storage3, tcp_tx_storage3, states[3].handle);
create_socket!(sockets, tcp_rx_storage4, tcp_tx_storage4, states[4].handle);
create_socket!(sockets, tcp_rx_storage5, tcp_tx_storage5, states[5].handle);
create_socket!(sockets, tcp_rx_storage6, tcp_tx_storage6, states[6].handle);
create_socket!(sockets, tcp_rx_storage7, tcp_tx_storage7, states[7].handle);
/// Contains a number of server sockets that get all sent the same for state in &mut states {
/// data (through `fmt::Write`). state.state = S::default();
pub struct Server<'a, 'b> { }
net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>,
sockets: SocketSet<'b, 'b, 'b>,
handles: [SocketHandle; SOCKET_COUNT],
}
impl<'a, 'b> Server<'a, 'b> {
/// Run a server with stack-allocated sockets
pub fn run<F>(net: EthernetInterface<'a, 'a, 'a, &'a mut stm32_eth::Eth<'static, 'static>>, f: F)
where
F: FnOnce(&mut Server<'a, '_>),
{
let mut sockets_storage: [_; SOCKET_COUNT] = Default::default();
let mut sockets = SocketSet::new(&mut sockets_storage[..]);
let mut handles: [SocketHandle; SOCKET_COUNT] = unsafe { MaybeUninit::uninit().assume_init() };
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, handles[0]);
create_socket!(sockets, tcp_rx_storage1, tcp_tx_storage1, handles[1]);
create_socket!(sockets, tcp_rx_storage2, tcp_tx_storage2, handles[2]);
create_socket!(sockets, tcp_rx_storage3, tcp_tx_storage3, handles[3]);
create_socket!(sockets, tcp_rx_storage4, tcp_tx_storage4, handles[4]);
create_socket!(sockets, tcp_rx_storage5, tcp_tx_storage5, handles[5]);
create_socket!(sockets, tcp_rx_storage6, tcp_tx_storage6, handles[6]);
create_socket!(sockets, tcp_rx_storage7, tcp_tx_storage7, handles[7]);
let mut server = Server { let mut server = Server {
handles, states,
sockets, sockets,
net, net,
}; };
@ -63,46 +71,21 @@ impl<'a, 'b> Server<'a, 'b> {
/// Poll the interface and the sockets /// Poll the interface and the sockets
pub fn poll(&mut self, now: Instant) -> Result<(), smoltcp::Error> { pub fn poll(&mut self, now: Instant) -> Result<(), smoltcp::Error> {
// Poll smoltcp EthernetInterface // Poll smoltcp EthernetInterface,
let mut poll_error = None; // pass only unexpected smoltcp errors to the caller
let activity = self.net.poll(&mut self.sockets, now) match self.net.poll(&mut self.sockets, now) {
.unwrap_or_else(|e| { Ok(_) => Ok(()),
poll_error = Some(e); Err(smoltcp::Error::Malformed) => Ok(()),
true Err(smoltcp::Error::Unrecognized) => Ok(()),
}); Err(e) => Err(e),
if activity {
// Listen on all sockets
for handle in &self.handles {
let mut socket = self.sockets.get::<TcpSocket>(*handle);
if ! socket.is_open() {
let _ = socket.listen(TCP_PORT);
}
} }
} }
// Pass some smoltcp errors to the caller /// Iterate over all sockets managed by this server
match poll_error { pub fn for_each<F: FnMut(SocketRef<TcpSocket>, &mut S)>(&mut self, mut callback: F) {
None => Ok(()), for state in &mut self.states {
Some(smoltcp::Error::Malformed) => Ok(()), let socket = self.sockets.get::<TcpSocket>(state.handle);
Some(smoltcp::Error::Unrecognized) => Ok(()), callback(socket, &mut state.state);
Some(e) => Err(e),
} }
} }
} }
/// Reusing the `fmt::Write` trait just for `write!()` convenience
impl<'a, 's> fmt::Write for Server<'a, 's> {
/// Write to all connected clients
fn write_str(&mut self, slice: &str) -> fmt::Result {
for handle in &self.handles {
let mut socket = self.sockets.get::<TcpSocket>(*handle);
if socket.can_send() {
// Ignore errors, proceed with next client
let _ = socket.write_str(slice);
}
}
Ok(())
}
}

151
src/session.rs Normal file
View File

@ -0,0 +1,151 @@
use core::ops::Deref;
use super::command_parser::{Command, Error as ParserError};
const MAX_LINE_LEN: usize = 64;
struct LineReader {
buf: [u8; MAX_LINE_LEN],
pos: usize,
}
impl LineReader {
pub fn new() -> Self {
LineReader {
buf: [0; MAX_LINE_LEN],
pos: 0,
}
}
pub fn feed(&mut self, c: u8) -> Option<LineResult> {
if c == 13 || c == 10 {
// Enter
if self.pos > 0 {
let len = self.pos;
self.pos = 0;
Some(LineResult {
buf: self.buf.clone(),
len,
})
} else {
None
}
} else if self.pos < self.buf.len() {
// Add input
self.buf[self.pos] = c;
self.pos += 1;
None
} else {
// Buffer is full, ignore
None
}
}
}
pub struct LineResult {
buf: [u8; MAX_LINE_LEN],
len: usize,
}
impl Deref for LineResult {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.buf[..self.len]
}
}
pub enum SessionOutput {
Nothing,
Command(Command),
Error(ParserError),
}
impl From<Result<Command, ParserError>> for SessionOutput {
fn from(input: Result<Command, ParserError>) -> Self {
input.map(SessionOutput::Command)
.unwrap_or_else(SessionOutput::Error)
}
}
pub const CHANNELS: usize = 2;
pub struct Session {
reader: LineReader,
reporting: bool,
report_pending: [bool; CHANNELS],
}
impl Default for Session {
fn default() -> Self {
Session::new()
}
}
impl Session {
pub fn new() -> Self {
Session {
reader: LineReader::new(),
reporting: false,
report_pending: [false; CHANNELS],
}
}
pub fn reset(&mut self) {
self.reader = LineReader::new();
self.reporting = false;
self.report_pending = [false; CHANNELS];
}
pub fn is_dirty(&self) -> bool {
self.reader.pos > 0
}
pub fn reporting(&self) -> bool {
self.reporting
}
pub fn set_report_pending(&mut self, channel: usize) {
if self.reporting {
self.report_pending[channel] = true;
}
}
pub fn is_report_pending(&self) -> Option<usize> {
if ! self.reporting {
None
} else {
self.report_pending.iter()
.enumerate()
.fold(None, |result, (channel, report_pending)| {
result.or_else(|| {
if *report_pending { Some(channel) } else { None }
})
})
}
}
pub fn mark_report_sent(&mut self, channel: usize) {
self.report_pending[channel] = false;
}
pub fn feed(&mut self, buf: &[u8]) -> (usize, SessionOutput) {
let mut buf_bytes = 0;
for (i, b) in buf.iter().enumerate() {
buf_bytes = i + 1;
let line = self.reader.feed(*b);
match line {
Some(line) => {
let command = Command::parse(&line);
match command {
Ok(Command::Reporting(reporting)) => {
self.reporting = reporting;
}
_ => {}
}
return (buf_bytes, command.into());
}
None => {}
}
}
(buf_bytes, SessionOutput::Nothing)
}
}