fan_monitor/src/config.rs
Steins7 3fc3dbaae8 Added config error screen
+ added error management
+ added config error when failing to load
2022-01-25 14:09:16 +01:00

317 lines
9.2 KiB
Rust

use core::fmt;
use crate::lcd_gui::LCDScreen;
//---Config struct----------------------------------------------------------------------------------
pub enum ConfigError {
LoadError,
}
#[derive(Copy, Clone)]
pub enum FanOutput {
P1,
P2,
}
#[derive(Copy, Clone)]
pub struct CalData {
pub offset: f32,
pub raw_value: Option<f32>,
}
impl CalData {
fn new() -> CalData {
CalData {
offset: 0.0,
raw_value: None,
}
}
}
#[derive(Copy, Clone)]
pub struct SystemConfig {
pub max_temp_diff: f32,
pub min_temp_diff: f32,
pub ext_offset: CalData,
pub p1_offset: CalData,
pub p2_offset: CalData,
pub fan_output: FanOutput,
}
impl SystemConfig {
pub fn new() -> SystemConfig {
SystemConfig {
max_temp_diff: 8.0,
min_temp_diff: 4.0,
ext_offset: CalData::new(),
p1_offset: CalData::new(),
p2_offset: CalData::new(),
fan_output: FanOutput::P1,
}
}
#[allow(dead_code)]
pub fn load() -> Result<SystemConfig, ConfigError> {
Err(ConfigError::LoadError)
}
#[allow(dead_code)]
pub fn store() -> SystemConfig {
unimplemented!();
}
}
//---Menu struct------------------------------------------------------------------------------------
const MENU_ENTRY_NB: usize = 6;
pub enum MenuState {
Scroll,
Entry,
}
pub struct Menu {
entries: [Entry; MENU_ENTRY_NB],
state: MenuState,
should_refresh: bool,
entry_index: usize,
}
impl Menu {
pub fn new(config: &SystemConfig) -> Menu {
Menu {
entries: [
Entry::new("fan out", EntryValue::FanOut (FanOutput::P1), config),
Entry::new("max diff", EntryValue::MaxTempDiff (0.0), config),
Entry::new("min diff", EntryValue::MinTempDiff (0.0), config),
Entry::new("ext cal", EntryValue::ExtOffset (CalData::new()), config),
Entry::new("p1 cal", EntryValue::P1Offset (CalData::new()), config),
Entry::new("p2 cal", EntryValue::P2Offset (CalData::new()), config),
],
state: MenuState::Scroll,
should_refresh: true,
entry_index: 0,
}
}
pub fn button_update(&mut self, config: &mut SystemConfig) -> bool {
self.should_refresh = true;
// update state machine
match self.state {
MenuState::Scroll => {
if self.entry_index == 0 { return true }
// read desired value from config
self.state = MenuState::Entry;
self.entries[self.entry_index - 1].select(config);
},
MenuState::Entry => {
// write modified value to config
self.state = MenuState::Scroll;
self.entries[self.entry_index - 1].deselect(config);
}
}
false
}
pub fn movement_update(&mut self, movement: i32) {
if movement != 0 {
match self.state {
MenuState::Scroll => {
// convert movement to entry index
let mut index = self.entry_index as i32 + movement;
if index > MENU_ENTRY_NB as i32 {
index = MENU_ENTRY_NB as i32;
} else if index < 0 {
index = 0;
}
self.entry_index = index as usize;
},
MenuState::Entry => {
// update entry
self.entries[self.entry_index - 1].update(-movement);
//note : "-" sign is just to revert encoder direction, feels better when using
//the interface
},
}
self.should_refresh = true;
}
}
pub fn reset(&mut self) {
match self.state {
MenuState::Entry => {
// if an entry is selected, disguard all modifications
self.entries[self.entry_index - 1].disgard();
self.state = MenuState::Scroll;
},
_ => (),
}
self.entry_index = 0;
}
pub fn display<L: LCDScreen>(&mut self, screen: &mut L) {
if self.should_refresh {
// first line
screen.clear();
screen.set_cursor_pos(0);
let _ = write!(screen, "\u{007e}");
if self.entry_index == 0 {
let _ = write!(screen, "retour");
} else {
self.entries[self.entry_index - 1].display(screen);
}
// second line (if any)
if self.entry_index < MENU_ENTRY_NB {
screen.set_cursor_pos(0x40);
let _ = write!(screen, " ");
self.entries[self.entry_index].display(screen);
}
self.should_refresh = false;
}
}
}
pub enum EntryValue {
MaxTempDiff (f32),
MinTempDiff (f32),
ExtOffset (CalData),
P1Offset (CalData),
P2Offset (CalData),
FanOut (FanOutput),
}
impl EntryValue {
fn modify(&mut self, n: i32) {
use libm::{powf, copysignf, fabsf};
use crate::utils::TEMP_RANGE;
match self {
Self::MaxTempDiff (val) | Self::MinTempDiff (val) => {
*val += copysignf(fabsf(n as f32) * 0.85 * powf(10.0, 0.15 * fabsf(n as f32))
* 0.1, n as f32);
// avoid display issues by limiting value range
if *val > 99.9 { *val = 99.9; }
else if *val < -99.9 { *val = -99.9; }
},
Self::ExtOffset (data) | Self::P1Offset (data) | Self::P2Offset (data) => {
data.offset += copysignf(fabsf(n as f32) * 0.85
* powf(10.0, 0.15 * fabsf(n as f32)) * 0.1, n as f32);
// avoid limiting issues like before, taking probe bounds into account
if data.offset + TEMP_RANGE.end > 99.9 {
data.offset = 99.9 - TEMP_RANGE.end;
}
else if data.offset + TEMP_RANGE.start < -99.9 {
data.offset = -99.9 - TEMP_RANGE.start;
}
},
Self::FanOut (output) => {
if n%2 != 0 {
match output {
FanOutput::P1 => *output = FanOutput::P2,
FanOutput::P2 => *output = FanOutput::P1,
}}}}
}
fn load(&mut self, config: &SystemConfig) {
match self {
Self::MaxTempDiff (val) => *val = config.max_temp_diff,
Self::MinTempDiff (val) => *val = config.min_temp_diff,
Self::ExtOffset (data) => *data = config.ext_offset,
Self::P1Offset (data) => *data = config.p1_offset,
Self::P2Offset (data) => *data = config.p2_offset,
Self::FanOut (val) => *val = config.fan_output,
};
}
fn store(&mut self, config: &mut SystemConfig) {
match self {
Self::MaxTempDiff (val) => config.max_temp_diff = *val,
Self::MinTempDiff (val) => config.min_temp_diff = *val,
Self::ExtOffset (data) => config.ext_offset = *data,
Self::P1Offset (data) => config.p1_offset = *data,
Self::P2Offset (data) => config.p2_offset = *data,
Self::FanOut (val) => config.fan_output = *val,
};
}
}
impl fmt::Display for EntryValue {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
use crate::lcd_gui::Temp;
match self {
Self::MaxTempDiff (val) | Self::MinTempDiff (val) =>
formatter.write_fmt(format_args!("{:.1}", val)),
Self::ExtOffset (data) | Self::P1Offset (data) | Self::P2Offset (data) => {
let temp: Temp = data.raw_value.map(|val| val + data.offset).into();
formatter.write_fmt(format_args!("{}", temp))
},
Self::FanOut (output) => {
match output {
FanOutput::P1 => formatter.write_fmt(format_args!("1")),
FanOutput::P2 => formatter.write_fmt(format_args!("2")),
}}}
}
}
pub struct Entry {
text: &'static str,
value: EntryValue,
is_selected: bool,
}
impl Entry {
pub fn new(text: &'static str, mut value: EntryValue, config: &SystemConfig) -> Entry {
value.load(config);
Entry {
text,
value,
is_selected: false,
}
}
pub fn display<B: core::fmt::Write>(&self, buffer: &mut B) {
let _ = match self.is_selected {
true => write!(buffer, "{}:{}", self.text, self.value),
false => write!(buffer, "{} {}", self.text, self.value),
};
}
pub fn select(&mut self, config: &SystemConfig) {
self.is_selected = true;
self.value.load(config);
}
pub fn deselect(&mut self, config: &mut SystemConfig) {
self.is_selected = false;
self.value.store(config);
}
pub fn disgard(&mut self) {
self.is_selected = false;
}
pub fn update(&mut self, n: i32) {
self.value.modify(n);
}
}