1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use std::time::Duration;

use log::debug;
use strum_macros::EnumIter;

use crate::brain::BrainFailure;

/// Which configuration of valves to use in order to generate the given outcome.
#[derive(PartialEq, Debug, Clone, EnumIter)]
pub enum HeatPumpMode {
    /// Heat only the tank.
    HotWaterOnly,
    /// As HotWaterOnly, except that the heat exchanger valve is open
    /// This should increase the flow through the heat pump and so improve efficiency,
    /// at the cost of losing some heat to the air and lower flow through the tank
    HeatingOnly,
    /// Heat both at the same time (mabe 60% hot water)
    MostlyHotWater,
    /// Heating, with some hot water spilling out of the top to potentially boost the heat pump
    /// OR HotWaterOnly, except that the heat exchanger valve is open to increase the flow through
    /// the heat pump and so improve efficiency, at the cost of losing some heat to the air and
    /// lower flow through the tank
    BoostedHeating,
    /// Heat pump off (and is blocked), secondary pump extracting heat out of the tank in order to cool the
    /// temperature.
    DrainTank,
    /// Neutral off state with nothing occurring.
    Off,
}

impl HeatPumpMode {
    pub fn is_hp_on(&self) -> bool {
        match self {
            HeatPumpMode::HotWaterOnly           => true,
            HeatPumpMode::HeatingOnly            => true,
            HeatPumpMode::MostlyHotWater         => true,
            HeatPumpMode::BoostedHeating         => true,
            HeatPumpMode::DrainTank              => false,
            HeatPumpMode::Off                    => false,
        }
    }

    pub fn is_hp_off(&self) -> bool {
        !self.is_hp_on()
    }
}

pub trait HeatPumpControl {
    fn try_set_heat_pump(&mut self, mode: HeatPumpMode) -> Result<(), BrainFailure>;

    fn try_get_heat_pump(&self) -> Result<HeatPumpMode, BrainFailure>;

    fn set_heat_pump(&mut self, mode: HeatPumpMode, debug_message: Option<&'static str>) -> Result<(), BrainFailure> {
        if self.try_get_heat_pump()? != mode {
            if let Some(debug_message) = debug_message {
                debug!("{debug_message}");
            }
            self.try_set_heat_pump(mode)?;
        }
        Ok(())
    }

    fn get_heat_pump_on_with_time(&self) -> Result<(bool, Duration), BrainFailure>;
}

pub trait HeatCirculationPumpControl {
    fn try_set_heat_circulation_pump(&mut self, on: bool) -> Result<(), BrainFailure>;

    fn try_get_heat_circulation_pump(&self) -> Result<bool, BrainFailure>;

    fn set_heat_circulation_pump(&mut self, on: bool, debug_message: Option<&'static str>) -> Result<(), BrainFailure> {
        if self.try_get_heat_circulation_pump()? != on {
            if let Some(debug_message) = debug_message {
                debug!("{debug_message}");
            }
            self.try_set_heat_circulation_pump(on)?;
        }
        Ok(())
    }
}

pub trait HeatingControl: HeatPumpControl + HeatCirculationPumpControl + Send + 'static {
    fn as_hp(&mut self) -> &mut dyn HeatPumpControl;

    fn as_cp(&mut self) -> &mut dyn HeatCirculationPumpControl;
}