Skip to content

Commit

Permalink
Make DHW Only mode check the slots properly each time instead of fixa…
Browse files Browse the repository at this point in the history
…ting
  • Loading branch information
leoleo73 committed Feb 24, 2024
1 parent b88fb58 commit f4e4e5c
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 85 deletions.
92 changes: 50 additions & 42 deletions src/brain/modes/dhw_only.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ use crate::time_util::timeslot::ZonedSlot;
use chrono::{DateTime, SecondsFormat, Utc};
use log::{debug, error, info, warn};
use std::fmt::{Display, Formatter};
use std::time::Duration;
use tokio::runtime::Runtime;

#[derive(Debug, PartialEq)]
pub struct DhwOnlyMode {
pub temps: DhwTemps,
pub expire: HeatUpEnd,
}

impl Mode for DhwOnlyMode {
Expand All @@ -40,9 +39,6 @@ impl Mode for DhwOnlyMode {
io_bundle: &mut IOBundle,
time: &impl TimeProvider,
) -> Result<Intention, BrainFailure> {
if self.expire.has_expired(time.get_utc_time()) {
return Ok(Intention::finish());
}
let temps = rt.block_on(info_cache.get_temps(io_bundle.temperature_manager()));
if temps.is_err() {
error!(
Expand All @@ -53,14 +49,29 @@ impl Mode for DhwOnlyMode {
}
let temps = temps.unwrap();

let temp = match temps.get(&self.temps.sensor) {
let now = time.get_utc_time();

let heating_control = expect_available!(io_bundle.heating_control())?;
let (_hp_on, hp_duration) = heating_control.get_heat_pump_on_with_time()?;
let short_duration = hp_duration < Duration::from_secs(60 * 10);

let slot = config.get_overrun_during().find_matching_slot(&now, &temps,
|temps, temp| temp < temps.max || (short_duration && temp < temps.extra.unwrap_or(temps.max))
);

let Some(slot) = slot else {
info!("No longer matches a DHW slot");
return Ok(Intention::finish());
};

let temp = match temps.get(&slot.temps.sensor) {
Some(temp) => temp,
None => {
error!("Sensor {} targeted by overrun didn't have a temperature associated.", self.temps.sensor);
error!("Sensor {} targeted by overrun didn't have a temperature associated.", slot.temps.sensor);
return Ok(Intention::off_now());
}
};
info!("Target: {} ({}), currently {:.2}", self.temps.max, self.expire, temp);
info!("Target: {}-{}/{:?} until {}, currently {:.2}", slot.temps.min, slot.temps.max, short_duration.then_some(slot.temps.extra), slot.slot, temp);

if info_cache.heating_on() {
match find_working_temp_action(
Expand All @@ -74,8 +85,8 @@ impl Mode for DhwOnlyMode {
debug!("Continuing to heat hot water as we would be circulating.");
}
Ok(WorkingTempAction::Heat { .. }) => {
info!("Call for heat during HeatUpTo, checking min {:.2?}", self.temps.min);
if *temp < self.temps.min {
info!("Call for heat during HeatUpTo, checking min {:.2?}", slot.temps.min);
if *temp < slot.temps.min {
info!("Below minimum - Ignoring call for heat");
} else {
return Ok(Intention::finish());
Expand All @@ -86,10 +97,6 @@ impl Mode for DhwOnlyMode {
}
};
}
if *temp > self.temps.max {
info!("Reached target overrun temp.");
return Ok(Intention::finish());
}

Ok(Intention::KeepState)
}
Expand Down Expand Up @@ -128,18 +135,8 @@ impl Display for HeatUpEnd {
}

impl DhwOnlyMode {
pub fn from_overrun(dhw: &DhwBap) -> Self {
Self {
temps: dhw.temps.clone(),
expire: HeatUpEnd::Slot(dhw.slot.clone()),
}
}

pub fn from_time(temps: DhwTemps, expire: DateTime<Utc>) -> Self {
Self {
temps,
expire: HeatUpEnd::Utc(expire),
}
pub fn new() -> Self {
Self {}
}
}

Expand All @@ -155,7 +152,7 @@ mod test {
use crate::io::temperatures::Sensor;
use crate::time_util::mytime::DummyTimeProvider;
use crate::time_util::test_utils::{date, time, utc_datetime, utc_time_slot};
use chrono::{Duration, TimeZone, Utc};
use chrono::{TimeZone, Utc};

#[test]
fn test_results() {
Expand All @@ -166,11 +163,14 @@ mod test {
WorkingRange::from_temp_only(WorkingTemperatureRange::from_delta(45.0, 10.0)),
);

let mut heat_up_to = DhwOnlyMode::from_overrun(&DhwBap::new(
utc_time_slot(10, 00, 00, 12, 00, 00),
40.0,
Sensor::TKBT,
));
let mut config = PythonBrainConfig::default();
config._add_dhw_slot(DhwBap {
slot: utc_time_slot(10, 00, 00, 12, 00, 00),
disable_below: None,
temps: DhwTemps { sensor: Sensor::TKBT, min: 10.0, max: 40.0, extra: None }
});

let mut heat_up_to = DhwOnlyMode::new();

let (mut io_bundle, mut io_handle) = new_dummy_io();

Expand All @@ -187,7 +187,7 @@ mod test {

let result = heat_up_to.update(
&rt,
&PythonBrainConfig::default(),
&config,
&mut info_cache,
&mut io_bundle,
&time_provider,
Expand All @@ -211,7 +211,7 @@ mod test {

let result = heat_up_to.update(
&rt,
&PythonBrainConfig::default(),
&config,
&mut info_cache,
&mut io_bundle,
&time_provider,
Expand All @@ -235,7 +235,7 @@ mod test {

let result = heat_up_to.update(
&rt,
&PythonBrainConfig::default(),
&config,
&mut info_cache,
&mut io_bundle,
&time_provider,
Expand All @@ -260,10 +260,15 @@ mod test {
);

let utc_time = utc_datetime(2023, 06, 12, 10, 00, 00);
let mut mode = DhwOnlyMode::from_time(
DhwTemps { sensor: Sensor::TKBT, min: 0.0, max: 39.0, extra: None },
utc_time + Duration::hours(1),
);

let mut config = PythonBrainConfig::default();
config._add_dhw_slot(DhwBap {
slot: utc_time_slot(09, 00, 00, 11, 00, 00),
disable_below: None,
temps: DhwTemps { sensor: Sensor::TKBT, min: 0.0, max: 39.0, extra: None }
});

let mut mode = DhwOnlyMode::new();

let rt = Runtime::new().unwrap();

Expand All @@ -277,7 +282,7 @@ mod test {

let next = mode.update(
&rt,
&PythonBrainConfig::default(),
&config,
&mut info_cache,
&mut io_bundle,
&time,
Expand All @@ -297,13 +302,17 @@ mod test {
let working_range = WorkingTemperatureRange::from_min_max(40.0, 50.0);

let utc_slot = utc_time_slot(12, 0, 0, 13, 0, 0);
let mut mode = DhwOnlyMode::from_overrun(&DhwBap::new_with_min(

let mut config = PythonBrainConfig::default();
config._add_dhw_slot(DhwBap::new_with_min(
utc_slot.clone(),
50.0,
Sensor::TKBT,
30.0,
));


let mut mode = DhwOnlyMode::new();
let rt = Runtime::new().unwrap();
let (mut io_bundle, mut handle) = new_dummy_io();
let time = DummyTimeProvider::in_slot(&utc_slot);
Expand All @@ -317,7 +326,6 @@ mod test {
HeatingState::ON,
WorkingRange::from_temp_only(working_range),
);
let config = PythonBrainConfig::default();

mode.enter(&config, &rt, &mut io_bundle)?;

Expand Down
6 changes: 3 additions & 3 deletions src/brain/modes/heating_mode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ fn get_heatup_while_off(
} else {
error!("Failed to retrieve sensor {} from temperatures when we really should have been able to.", bap.temps.sensor)
}
return Some(HeatingMode::DhwOnly(DhwOnlyMode::from_overrun(bap)));
return Some(HeatingMode::DhwOnly(DhwOnlyMode::new()));
}
None
}
Expand Down Expand Up @@ -475,7 +475,7 @@ pub fn handle_finish_mode(
|temps, temp| temp < temps.max);
if let Some(slot) = slot {
debug!("Overrun: {slot:?} would apply, going into overrun instead of circulating.");
return Ok(Some(HeatingMode::DhwOnly(DhwOnlyMode::from_overrun(slot))));
return Ok(Some(HeatingMode::DhwOnly(DhwOnlyMode::new())));
}

if !circulate {
Expand Down Expand Up @@ -525,7 +525,7 @@ pub fn handle_finish_mode(
|temps, temp| temp < temps.max || (hp_duration < Duration::from_secs(60 * 10) && temp < temps.extra.unwrap_or(temps.max))
);
if let Some(slot) = slot {
return Ok(Some(HeatingMode::DhwOnly(DhwOnlyMode::from_overrun(slot))));
return Ok(Some(HeatingMode::DhwOnly(DhwOnlyMode::new())));
}
Ok(Some(HeatingMode::off()))
}
Expand Down
11 changes: 5 additions & 6 deletions src/brain/modes/heating_mode/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,11 @@ pub fn test_transitions() -> Result<(), BrainFailure> {
HeatingMode::Circulate(CirculateMode::default()),
HeatingMode::On(OnMode::default()),
)?;


test_transition_between(
HeatingMode::DhwOnly(DhwOnlyMode::from_time(
DhwTemps { sensor: Sensor::TKBT, min: 0.0, max: 47.0, extra: None },
Utc::now(),
)),
HeatingMode::DhwOnly(DhwOnlyMode::new()),
//DhwTemps { sensor: Sensor::TKBT, min: 0.0, max: 47.0, extra: None }, NOW
HeatingMode::off(),
)?;

Expand Down Expand Up @@ -338,8 +338,7 @@ fn test_overrun_scenarios() {
println!("Mode: {:?}", mode);
assert!(mode.is_some());
if let HeatingMode::DhwOnly(heat_up_to) = mode.unwrap() {
assert_eq!(heat_up_to.temps.sensor, Sensor::TKBT);
assert_eq!(heat_up_to.temps.max, 46.0) // Fine to have this lower of the two as it will increase anyway if needed.
// Nothing else to check
} else {
panic!("Should have been heat up to mode.")
}
Expand Down
27 changes: 4 additions & 23 deletions src/brain/modes/on.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,29 +93,10 @@ impl Mode for OnMode {
let temps = temps.unwrap();

if !info_cache.heating_on() {
// TODO: 6 minute / overrun should move to Intention / tracking out of state.
let running_for = self.started.elapsed();
let min_runtime = config.get_min_hp_runtime();
if running_for < *min_runtime.get_min_runtime() {
warn!(
"Warning: Carrying on until the {} second mark or safety cut off: {}",
min_runtime.get_min_runtime().as_secs(),
min_runtime.get_safety_cut_off()
);
let remaining = *min_runtime.get_min_runtime() - running_for;
let end = time.get_utc_time() + chrono::Duration::from_std(remaining).unwrap();
return Ok(Intention::SwitchForce(HeatingMode::DhwOnly(
DhwOnlyMode::from_time(
DhwTemps {
sensor: min_runtime.get_safety_cut_off().get_target_sensor().clone(),
min: 0.0,
max: min_runtime.get_safety_cut_off().get_target_temp(),
extra: None,
},
end
)
)));
}
// Finish mode should pick up any overrun whether considering
// minimum run time or not.
// TODO: config.get_min_hp_runtime();
// min_runtime.get_safety_cut_off().get_target_sensor().clone(),
return Ok(Intention::finish());
}

Expand Down
4 changes: 4 additions & 0 deletions src/brain/python_like/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ impl PythonBrainConfig {
pub fn get_min_hp_runtime(&self) -> &MinHeatPumpRuntime {
&self.min_hp_runtime
}

pub fn _add_dhw_slot(&mut self, slot: overrun_config::DhwBap) {
self.additive_config.overrun_during.slots.push(slot);
}
}

impl Default for PythonBrainConfig {
Expand Down
8 changes: 4 additions & 4 deletions src/brain/python_like/config/overrun_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::fmt::{Display, Formatter};

#[derive(Deserialize, Clone, Debug, PartialEq, Default)]
pub struct OverrunConfig {
slots: Vec<DhwBap>,
pub slots: Vec<DhwBap>,
}

impl OverrunConfig {
Expand Down Expand Up @@ -148,13 +148,13 @@ impl DhwBap {
}

#[cfg(test)]
pub fn new_with_min(slot: ZonedSlot, temp: f32, sensor: Sensor, min_temp: f32) -> Self {
assert!(min_temp < temp, "min_temp should be less than temp");
pub fn new_with_min(slot: ZonedSlot, max_temp: f32, sensor: Sensor, min_temp: f32) -> Self {
assert!(min_temp < max_temp, "min_temp should be less than max_temp");
Self {
slot,
disable_below: None,
temps: DhwTemps {
sensor, min: min_temp, max: temp, extra: None
sensor, min: min_temp, max: max_temp, extra: None
}
}
}
Expand Down
18 changes: 11 additions & 7 deletions src/brain/python_like/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,17 @@ temps = { sensor = "TKBT", min = 30.0, max = 55.0 }
#[test_log::test]
fn test_ignore_wiser_into_overrun() -> Result<(), BrainFailure> {
let rt = Runtime::new().expect("Failed to create runtime.");
let config =
let mut config: PythonBrainConfig =
toml::from_str(IGNORE_WISER_OVERRUN_CONFIG_STR).expect("Failed to deserialize config");

// TODO: Include in config above
config._add_dhw_slot(DhwBap::new_with_min(
utc_time_slot(13, 00, 00, 15, 00, 00),
55.0,
Sensor::TKBT,
30.0)
);

let mut brain = PythonBrain::new(config);
let (mut io_bundle, mut handle) = new_dummy_io();

Expand Down Expand Up @@ -182,12 +191,7 @@ fn test_ignore_wiser_into_overrun() -> Result<(), BrainFailure> {
);
brain.run(&rt, &mut io_bundle, &time_provider)?;

let expected_mode = HeatingMode::DhwOnly(DhwOnlyMode::from_overrun(&DhwBap::new_with_min(
utc_time_slot(13, 00, 00, 15, 00, 00),
55.0,
Sensor::TKBT,
30.0,
)));
let expected_mode = HeatingMode::DhwOnly(DhwOnlyMode::new());
assert_eq!(brain.heating_mode, Some(expected_mode));

Ok(())
Expand Down

0 comments on commit f4e4e5c

Please sign in to comment.