grid_tariffs/
power_tariffs.rs

1use chrono::DateTime;
2use chrono_tz::Tz;
3use serde::Serialize;
4
5use crate::{
6    costs::{CostPeriods, CostPeriodsSimple, LoadType},
7    money::Money,
8};
9
10#[derive(Debug, Clone, Serialize)]
11#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
12#[serde(tag = "type", content = "value")]
13pub enum PowerTariff {
14    Unverified,
15    NotImplemented,
16    Periods {
17        method: TariffCalculationMethod,
18        periods: CostPeriods,
19    },
20}
21
22impl PowerTariff {
23    pub const fn is_unverified(&self) -> bool {
24        matches!(self, Self::Unverified)
25    }
26
27    pub(super) const fn new(method: TariffCalculationMethod, periods: CostPeriods) -> Self {
28        Self::Periods { method, periods }
29    }
30
31    pub(super) fn kw_cost(
32        &self,
33        timestamp: DateTime<Tz>,
34        fuse_size: u16,
35        yearly_consumption: u32,
36    ) -> Money {
37        let cost = Money::ZERO;
38        match self {
39            PowerTariff::Unverified | PowerTariff::NotImplemented => cost,
40            PowerTariff::Periods { method, periods } => {
41                for period in periods.iter() {
42                    let money = period.cost().cost_for(fuse_size, yearly_consumption);
43                }
44                cost
45            }
46        }
47    }
48
49    pub fn simplified(&self, fuse_size: u16, yearly_consumption: u32) -> PowerTariffSimplified {
50        PowerTariffSimplified::new(self, fuse_size, yearly_consumption)
51    }
52}
53
54/// The method used to calculate power tariffs
55#[derive(Debug, Clone, Copy, Serialize)]
56#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
57pub enum TariffCalculationMethod {
58    /// Power peak for top hour of the top three days of the month
59    AverageDays(u8),
60    /// Average of top three hours of the month
61    AverageHours(u8),
62    /// Like AverageDays, but one for base load time and one for peak load time
63    AverageDaysDifferentiated {
64        peak: u8,
65        base: u8,
66    },
67    /// Only count the max peak hour of the month
68    PeakHour,
69    // Count one peak hour per month, per specified load type
70    PeakHours(&'static [LoadType]),
71    /// Daytime and nighttime are calculated with different values
72    // TODO: How can this be extracted from CostPeriods...?!
73    AverageDayNightDifferentiated {
74        day: i32,
75        night: i32,
76    },
77}
78
79impl TariffCalculationMethod {
80    pub(super) fn relevant_samples(
81        &self,
82        grid_consumption: Vec<GridConsumption>,
83    ) -> Vec<GridConsumption> {
84        //     match self {
85        //         TariffCalculationMethod::Unimplemented => vec![],
86        //         TariffCalculationMethod::AverageDays(n) => grid_consumption
87        //             .iter()
88        //             .into_group_map_by(|pm| {
89        //                 pm.timestamp()
90        //                     .with_minute(0)
91        //                     .with_second(0)
92        //                     .with_nanosecond(0)
93        //             })
94        //             .into_iter()
95        //             .map(|(_day_hour, pm)| pm.into_iter().max_by_key(|pm| pm.value()).unwrap())
96        //             .copied()
97        //             .take((*n).into())
98        //             .collect_vec(),
99        //         TariffCalculationMethod::AverageHours(n) => {
100        //             grid_consumption.sort_by_key(|hp| hp.value());
101        //             grid_consumption.into_iter().take((*n).into()).collect()
102        //         }
103        //         TariffCalculationMethod::AverageDaysDifferentiated { .. } => todo!(),
104        //         TariffCalculationMethod::AverageDayNightDifferentiated { .. } => todo!(),
105        //         TariffCalculationMethod::PeakHour => grid_consumption
106        //             .iter()
107        //             .max_by_key(|dw| dw.value())
108        //             .map(|dw| vec![*dw])
109        //             .unwrap_or_default(),
110        //     }
111        todo!()
112    }
113}
114
115#[derive(Clone, Copy)]
116pub(super) struct HourPower(DateTime<Tz>, u32);
117
118impl HourPower {
119    fn timestamp(&self) -> DateTime<Tz> {
120        self.0
121    }
122
123    fn watts(&self) -> u32 {
124        self.1
125    }
126}
127
128// // Tekniska Verken Linköping, prislista alternativ
129// // Skulle ge:
130pub struct Peak {
131    /// Non-inclusive time period
132    time_period: (DateTime<Tz>, DateTime<Tz>),
133    current_max_w: u32,
134    cost_per_kw: Money,
135    kw_divider: u8,
136}
137
138impl Peak {
139    // pub fn kwh_cost(&self, _resolution: Resolution) -> Money {
140    //     self.cost_per_kw
141    // }
142
143    pub fn contains(&self, timestamp: DateTime<Tz>) -> bool {
144        timestamp >= self.time_period.0 && timestamp < self.time_period.1
145    }
146}
147
148impl PowerTariff {
149    pub fn get_peaks(
150        &self,
151        time_period: (DateTime<Tz>, DateTime<Tz>),
152        grid_consumption: Vec<GridConsumption>,
153    ) -> Option<Vec<Peak>> {
154        let Self::Periods { method, periods } = self else {
155            return None;
156        };
157        let _samples = method.relevant_samples(grid_consumption);
158        todo!()
159    }
160}
161
162#[derive(Debug, Clone, Copy)]
163pub struct GridConsumption {
164    timestamp: DateTime<Tz>,
165    wh: u32,
166}
167
168/// Like PowerTariff, but with costs being simple Money objects
169#[derive(Debug, Clone, Serialize)]
170#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
171#[serde(tag = "type", content = "value")]
172pub enum PowerTariffSimplified {
173    Unverified,
174    NotImplemented,
175    Periods {
176        method: TariffCalculationMethod,
177        periods: CostPeriodsSimple,
178    },
179}
180
181impl PowerTariffSimplified {
182    fn new(fee: &PowerTariff, fuse_size: u16, yearly_consumption: u32) -> Self {
183        match *fee {
184            PowerTariff::Unverified => PowerTariffSimplified::Unverified,
185            PowerTariff::NotImplemented => PowerTariffSimplified::NotImplemented,
186            PowerTariff::Periods { method, periods } => PowerTariffSimplified::Periods {
187                method,
188                periods: CostPeriodsSimple::new(periods, fuse_size, yearly_consumption),
189            },
190        }
191    }
192}