From e6a5c31db60cd1f27606eae3c85cdaf94846f0ea Mon Sep 17 00:00:00 2001 From: topquark12 Date: Mon, 7 Jun 2021 10:07:05 +0800 Subject: [PATCH] main.rs refactor to reduce length (#60) Move command handling to command_handler.rs to shorten main.rs Reviewed-on: https://git.m-labs.hk/M-Labs/thermostat/pulls/60 Co-authored-by: topquark12 Co-committed-by: topquark12 --- src/command_handler.rs | 373 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 245 ++------------------------- 2 files changed, 384 insertions(+), 234 deletions(-) create mode 100644 src/command_handler.rs diff --git a/src/command_handler.rs b/src/command_handler.rs new file mode 100644 index 0000000..aa3b7d1 --- /dev/null +++ b/src/command_handler.rs @@ -0,0 +1,373 @@ +use smoltcp::socket::TcpSocket; +use log::{error, warn}; +use core::fmt::Write; +use super::{ + net, + command_parser::{ + Ipv4Config, + Command, + ShowCommand, + CenterPoint, + PidParameter, + PwmPin, + ShParameter + }, + leds::Leds, + ad7172, + CHANNEL_CONFIG_KEY, + channels::{ + Channels, + CHANNELS + }, + config::ChannelConfig, + dfu, + flash_store::FlashStore, + session::Session +}; + +use uom::{ + si::{ + f64::{ + ElectricCurrent, + ElectricPotential, + ElectricalResistance, + ThermodynamicTemperature, + }, + electric_current::ampere, + electric_potential::volt, + electrical_resistance::ohm, + thermodynamic_temperature::degree_celsius, + }, +}; + +#[derive(Debug, Clone, PartialEq)] +pub enum Handler { + Handled, + CloseSocket, + NewIPV4(Ipv4Config), + Reset, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + ReportError, + PostFilterRateError, + FlashError +} + +fn send_line(socket: &mut TcpSocket, data: &[u8]) -> bool { + let send_free = socket.send_capacity() - socket.send_queue(); + if data.len() > send_free + 1 { + // Not enough buffer space, skip report for now, + // instead of sending incomplete line + warn!( + "TCP socket has only {}/{} needed {}", + send_free + 1, socket.send_capacity(), data.len(), + ); + } else { + match socket.send_slice(&data) { + Ok(sent) if sent == data.len() => { + let _ = socket.send_slice(b"\n"); + // success + return true + } + Ok(sent) => + warn!("sent only {}/{} bytes", sent, data.len()), + Err(e) => + error!("error sending line: {:?}", e), + } + } + // not success + false +} + +impl Handler { + + fn reporting(socket: &mut TcpSocket) -> Result { + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn show_report_mode(socket: &mut TcpSocket, session: &Session) -> Result { + let _ = writeln!(socket, "{{ \"report\": {:?} }}", session.reporting()); + Ok(Handler::Handled) + } + + fn show_report(socket: &mut TcpSocket, channels: &mut Channels) -> Result { + match channels.reports_json() { + Ok(buf) => { + send_line(socket, &buf[..]); + } + Err(e) => { + error!("unable to serialize report: {:?}", e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::ReportError); + } + } + Ok(Handler::Handled) + } + + fn show_pid(socket: &mut TcpSocket, channels: &mut Channels) -> Result { + match channels.pid_summaries_json() { + Ok(buf) => { + send_line(socket, &buf); + } + Err(e) => { + error!("unable to serialize pid summary: {:?}", e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::ReportError); + } + } + Ok(Handler::Handled) + } + + fn show_pwm(socket: &mut TcpSocket, channels: &mut Channels) -> Result { + match channels.pwm_summaries_json() { + Ok(buf) => { + send_line(socket, &buf); + } + Err(e) => { + error!("unable to serialize pwm summary: {:?}", e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::ReportError); + } + } + Ok(Handler::Handled) + } + + fn show_steinhart_hart(socket: &mut TcpSocket, channels: &mut Channels) -> Result { + match channels.steinhart_hart_summaries_json() { + Ok(buf) => { + send_line(socket, &buf); + } + Err(e) => { + error!("unable to serialize steinhart-hart summaries: {:?}", e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::ReportError); + } + } + Ok(Handler::Handled) + } + + fn show_post_filter (socket: &mut TcpSocket, channels: &mut Channels) -> Result { + match channels.postfilter_summaries_json() { + Ok(buf) => { + send_line(socket, &buf); + } + Err(e) => { + error!("unable to serialize postfilter summary: {:?}", e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::ReportError); + } + } + Ok(Handler::Handled) + } + + fn show_ipv4 (socket: &mut TcpSocket, ipv4_config: &mut Ipv4Config) -> Result { + let (cidr, gateway) = net::split_ipv4_config(ipv4_config.clone()); + let _ = write!(socket, "{{\"addr\":\"{}\"", cidr); + gateway.map(|gateway| write!(socket, ",\"gateway\":\"{}\"", gateway)); + let _ = writeln!(socket, "}}"); + Ok(Handler::Handled) + } + + fn engage_pid (socket: &mut TcpSocket, channels: &mut Channels, leds: &mut Leds, channel: usize) -> Result { + channels.channel_state(channel).pid_engaged = true; + leds.g3.on(); + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, leds: &mut Leds, channel: usize, pin: PwmPin, value: f64) -> Result { + match pin { + PwmPin::ISet => { + channels.channel_state(channel).pid_engaged = false; + leds.g3.off(); + let current = ElectricCurrent::new::(value); + channels.set_i(channel, current); + channels.power_up(channel); + } + PwmPin::MaxV => { + let voltage = ElectricPotential::new::(value); + channels.set_max_v(channel, voltage); + } + PwmPin::MaxIPos => { + let current = ElectricCurrent::new::(value); + channels.set_max_i_pos(channel, current); + } + PwmPin::MaxINeg => { + let current = ElectricCurrent::new::(value); + channels.set_max_i_neg(channel, current); + } + } + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn set_center_point(socket: &mut TcpSocket, channels: &mut Channels, channel: usize, center: CenterPoint) -> Result { + let i_tec = channels.get_i(channel); + let state = channels.channel_state(channel); + state.center = center; + if !state.pid_engaged { + channels.set_i(channel, i_tec); + } + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn set_pid (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, parameter: PidParameter, value: f64) -> Result { + let pid = &mut channels.channel_state(channel).pid; + use super::command_parser::PidParameter::*; + match parameter { + Target => + pid.target = value, + KP => + pid.parameters.kp = value as f32, + KI => + pid.update_ki(value as f32), + KD => + pid.parameters.kd = value as f32, + OutputMin => + pid.parameters.output_min = value as f32, + OutputMax => + pid.parameters.output_max = value as f32, + IntegralMin => + pid.parameters.integral_min = value as f32, + IntegralMax => + pid.parameters.integral_max = value as f32, + } + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn set_steinhart_hart (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, parameter: ShParameter, value: f64) -> Result { + let sh = &mut channels.channel_state(channel).sh; + use super::command_parser::ShParameter::*; + match parameter { + T0 => sh.t0 = ThermodynamicTemperature::new::(value), + B => sh.b = value, + R0 => sh.r0 = ElectricalResistance::new::(value), + } + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn reset_post_filter (socket: &mut TcpSocket, channels: &mut Channels, channel: usize) -> Result { + channels.adc.set_postfilter(channel as u8, None).unwrap(); + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + + fn set_post_filter (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, rate: f32) -> Result { + let filter = ad7172::PostFilter::closest(rate); + match filter { + Some(filter) => { + channels.adc.set_postfilter(channel as u8, Some(filter)).unwrap(); + send_line(socket, b"{}"); + } + None => { + error!("unable to choose postfilter for rate {:.3}", rate); + send_line(socket, b"{{\"error\": \"unable to choose postfilter rate\"}}"); + return Err(Error::PostFilterRateError); + } + } + Ok(Handler::Handled) + } + + fn load_channel (socket: &mut TcpSocket, channels: &mut Channels, store: &mut FlashStore, channel: Option) -> Result { + for c in 0..CHANNELS { + if channel.is_none() || channel == Some(c) { + match store.read_value::(CHANNEL_CONFIG_KEY[c]) { + Ok(Some(config)) => { + config.apply(channels, c); + send_line(socket, b"{}"); + } + Ok(None) => { + error!("flash config not found"); + send_line(socket, b"{{\"error\": \"flash config not found\"}}"); + } + Err(e) => { + error!("unable to load config from flash: {:?}", e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::FlashError); + } + } + } + } + Ok(Handler::Handled) + } + + fn save_channel (socket: &mut TcpSocket, channels: &mut Channels, channel: Option, store: &mut FlashStore) -> Result { + for c in 0..CHANNELS { + let mut store_value_buf = [0u8; 256]; + if channel.is_none() || channel == Some(c) { + let config = ChannelConfig::new(channels, c); + match store.write_value(CHANNEL_CONFIG_KEY[c], &config, &mut store_value_buf) { + Ok(()) => { + send_line(socket, b"{}"); + } + Err(e) => { + error!("unable to save channel {} config to flash: {:?}", c, e); + let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); + return Err(Error::FlashError); + } + } + } + } + Ok(Handler::Handled) + } + + fn set_ipv4 (socket: &mut TcpSocket, store: &mut FlashStore, config: Ipv4Config) -> Result { + let _ = store + .write_value("ipv4", &config, [0; 16]) + .map_err(|e| error!("unable to save ipv4 config to flash: {:?}", e)); + let new_ipv4_config = Some(config); + send_line(socket, b"{}"); + Ok(Handler::NewIPV4(new_ipv4_config.unwrap())) + } + + fn reset (channels: &mut Channels) -> Result { + for i in 0..CHANNELS { + channels.power_down(i); + } + // should_reset = true; + Ok(Handler::Reset) + } + + fn dfu (channels: &mut Channels) -> Result { + for i in 0..CHANNELS { + channels.power_down(i); + } + unsafe { + dfu::set_dfu_trigger(); + } + // should_reset = true; + Ok(Handler::Reset) + } + + pub fn handle_command (command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, leds: &mut Leds, store: &mut FlashStore, ipv4_config: &mut Ipv4Config) -> Result { + match command { + Command::Quit => Ok(Handler::CloseSocket), + Command::Reporting(_reporting) => Handler::reporting(socket), + Command::Show(ShowCommand::Reporting) => Handler::show_report_mode(socket, session), + Command::Show(ShowCommand::Input) => Handler::show_report(socket, channels), + Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, channels), + Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels), + Command::Show(ShowCommand::SteinhartHart) => Handler::show_steinhart_hart(socket, channels), + Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels), + Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config), + Command::PwmPid { channel } => Handler::engage_pid(socket, channels, leds, channel), + Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, leds, channel, pin, value), + Command::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center), + Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value), + Command::SteinhartHart { channel, parameter, value } => Handler::set_steinhart_hart(socket, channels, channel, parameter, value), + Command::PostFilter { channel, rate: None } => Handler::reset_post_filter(socket, channels, channel), + Command::PostFilter { channel, rate: Some(rate) } => Handler::set_post_filter(socket, channels, channel, rate), + Command::Load { channel } => Handler::load_channel(socket, channels, store, channel), + Command::Save { channel } => Handler::save_channel(socket, channels, channel, store), + Command::Ipv4(config) => Handler::set_ipv4(socket, store, config), + Command::Reset => Handler::reset(channels), + Command::Dfu => Handler::dfu(channels) + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9a11b7d..246e20d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ use panic_semihosting as _; use log::{error, info, warn}; -use core::fmt::Write; use cortex_m::asm::wfi; use cortex_m_rt::entry; use stm32f4xx_hal::{ @@ -26,20 +25,6 @@ use smoltcp::{ socket::TcpSocket, wire::EthernetAddress, }; -use uom::{ - si::{ - f64::{ - ElectricCurrent, - ElectricPotential, - ElectricalResistance, - ThermodynamicTemperature, - }, - electric_current::ampere, - electric_potential::volt, - electrical_resistance::ohm, - thermodynamic_temperature::degree_celsius, - }, -}; mod init_log; use init_log::init_log; @@ -55,7 +40,7 @@ use server::Server; mod session; use session::{Session, SessionInput}; mod command_parser; -use command_parser::{Command, Ipv4Config, PwmPin, ShowCommand}; +use command_parser::Ipv4Config; mod timer; mod pid; mod steinhart_hart; @@ -67,6 +52,8 @@ mod config; use config::ChannelConfig; mod flash_store; mod dfu; +mod command_handler; +use command_handler::Handler; const HSE: MegaHertz = MegaHertz(8); #[cfg(not(feature = "semihosting"))] @@ -149,7 +136,7 @@ fn main() -> ! { usb::State::setup(usb); let mut store = flash_store::store(dp.FLASH); - let mut store_value_buf = [0u8; 256]; + let mut channels = Channels::new(pins); for c in 0..CHANNELS { @@ -218,223 +205,13 @@ fn main() -> ! { // socket RX ring buffer wraps around, or when the command is sent as seperate TCP packets etc. // Do nothing and feed more data to the line reader in the next loop cycle. Ok(SessionInput::Nothing) => {} - Ok(SessionInput::Command(command)) => match command { - Command::Quit => - socket.close(), - Command::Reporting(_reporting) => { - // handled by session - send_line(&mut socket, b"{}"); - } - Command::Show(ShowCommand::Reporting) => { - let _ = writeln!(socket, "{{ \"report\": {:?} }}", session.reporting()); - } - Command::Show(ShowCommand::Input) => { - match channels.reports_json() { - Ok(buf) => { - send_line(&mut socket, &buf[..]); - } - Err(e) => { - error!("unable to serialize report: {:?}", e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - - } - } - } - Command::Show(ShowCommand::Pid) => { - match channels.pid_summaries_json() { - Ok(buf) => { - send_line(&mut socket, &buf); - } - Err(e) => { - error!("unable to serialize pid summary: {:?}", e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - } - } - } - Command::Show(ShowCommand::Pwm) => { - match channels.pwm_summaries_json() { - Ok(buf) => { - send_line(&mut socket, &buf); - } - Err(e) => { - error!("unable to serialize pwm summary: {:?}", e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - } - } - } - Command::Show(ShowCommand::SteinhartHart) => { - match channels.steinhart_hart_summaries_json() { - Ok(buf) => { - send_line(&mut socket, &buf); - } - Err(e) => { - error!("unable to serialize steinhart-hart summaries: {:?}", e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - } - } - } - Command::Show(ShowCommand::PostFilter) => { - match channels.postfilter_summaries_json() { - Ok(buf) => { - send_line(&mut socket, &buf); - } - Err(e) => { - error!("unable to serialize postfilter summary: {:?}", e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - } - } - } - Command::Show(ShowCommand::Ipv4) => { - let (cidr, gateway) = net::split_ipv4_config(ipv4_config.clone()); - let _ = write!(socket, "{{\"addr\":\"{}\"", cidr); - gateway.map(|gateway| write!(socket, ",\"gateway\":\"{}\"", gateway)); - let _ = writeln!(socket, "}}"); - } - Command::PwmPid { channel } => { - channels.channel_state(channel).pid_engaged = true; - leds.g3.on(); - send_line(&mut socket, b"{}"); - } - Command::Pwm { channel, pin, value } => { - match pin { - PwmPin::ISet => { - channels.channel_state(channel).pid_engaged = false; - leds.g3.off(); - let current = ElectricCurrent::new::(value); - channels.set_i(channel, current); - channels.power_up(channel); - } - PwmPin::MaxV => { - let voltage = ElectricPotential::new::(value); - channels.set_max_v(channel, voltage); - } - PwmPin::MaxIPos => { - let current = ElectricCurrent::new::(value); - channels.set_max_i_pos(channel, current); - } - PwmPin::MaxINeg => { - let current = ElectricCurrent::new::(value); - channels.set_max_i_neg(channel, current); - } - } - send_line(&mut socket, b"{}"); - } - Command::CenterPoint { channel, center } => { - let i_tec = channels.get_i(channel); - let state = channels.channel_state(channel); - state.center = center; - if !state.pid_engaged { - channels.set_i(channel, i_tec); - } - send_line(&mut socket, b"{}"); - } - Command::Pid { channel, parameter, value } => { - let pid = &mut channels.channel_state(channel).pid; - use command_parser::PidParameter::*; - match parameter { - Target => - pid.target = value, - KP => - pid.parameters.kp = value as f32, - KI => - pid.update_ki(value as f32), - KD => - pid.parameters.kd = value as f32, - OutputMin => - pid.parameters.output_min = value as f32, - OutputMax => - pid.parameters.output_max = value as f32, - IntegralMin => - pid.parameters.integral_min = value as f32, - IntegralMax => - pid.parameters.integral_max = value as f32, - } - send_line(&mut socket, b"{}"); - } - Command::SteinhartHart { channel, parameter, value } => { - let sh = &mut channels.channel_state(channel).sh; - use command_parser::ShParameter::*; - match parameter { - T0 => sh.t0 = ThermodynamicTemperature::new::(value), - B => sh.b = value, - R0 => sh.r0 = ElectricalResistance::new::(value), - } - send_line(&mut socket, b"{}"); - } - Command::PostFilter { channel, rate: None } => { - channels.adc.set_postfilter(channel as u8, None).unwrap(); - send_line(&mut socket, b"{}"); - } - Command::PostFilter { channel, rate: Some(rate) } => { - let filter = ad7172::PostFilter::closest(rate); - match filter { - Some(filter) => { - channels.adc.set_postfilter(channel as u8, Some(filter)).unwrap(); - send_line(&mut socket, b"{}"); - } - None => { - error!("unable to choose postfilter for rate {:.3}", rate); - send_line(&mut socket, b"{{\"error\": \"unable to choose postfilter rate\"}}"); - } - } - } - Command::Load { channel } => { - for c in 0..CHANNELS { - if channel.is_none() || channel == Some(c) { - match store.read_value::(CHANNEL_CONFIG_KEY[c]) { - Ok(Some(config)) => { - config.apply(&mut channels, c); - send_line(&mut socket, b"{}"); - } - Ok(None) => { - error!("flash config not found"); - send_line(&mut socket, b"{{\"error\": \"flash config not found\"}}"); - } - Err(e) => { - error!("unable to load config from flash: {:?}", e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - } - } - } - } - } - Command::Save { channel } => { - for c in 0..CHANNELS { - if channel.is_none() || channel == Some(c) { - let config = ChannelConfig::new(&mut channels, c); - match store.write_value(CHANNEL_CONFIG_KEY[c], &config, &mut store_value_buf) { - Ok(()) => { - send_line(&mut socket, b"{}"); - } - Err(e) => { - error!("unable to save channel {} config to flash: {:?}", c, e); - let _ = writeln!(socket, "{{\"error\":\"{:?}\"}}", e); - } - } - } - } - } - Command::Ipv4(config) => { - let _ = store - .write_value("ipv4", &config, [0; 16]) - .map_err(|e| error!("unable to save ipv4 config to flash: {:?}", e)); - new_ipv4_config = Some(config); - send_line(&mut socket, b"{}"); - } - Command::Reset => { - for i in 0..CHANNELS { - channels.power_down(i); - } - should_reset = true; - } - Command::Dfu => { - for i in 0..CHANNELS { - channels.power_down(i); - } - unsafe { - dfu::set_dfu_trigger(); - } - should_reset = true; + Ok(SessionInput::Command(command)) => { + match Handler::handle_command(command, &mut socket, &mut channels, session, &mut leds, &mut store, &mut ipv4_config) { + Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip), + Ok(Handler::Handled) => {}, + Ok(Handler::CloseSocket) => socket.close(), + Ok(Handler::Reset) => should_reset = true, + Err(_) => {}, } } Ok(SessionInput::Error(e)) => {