1use chrono::DateTime;
2use serde::Serialize;
3
4use crate::{
5 Actual, GridOperator, Language, PartialPowerAverage, PowerAverage, PowerTariffMatches,
6 costs::{CostPeriods, CostPeriodsSimple},
7 hours::Hours,
8};
9
10#[derive(Debug, Clone, Copy, Serialize)]
11#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
12pub struct PowerDivide {
13 pub(crate) hours: Option<Hours>,
14 divider: u8,
15}
16
17impl PowerDivide {
18 pub const fn new(hours: Option<Hours>, divider: u8) -> Self {
19 Self { hours, divider }
20 }
21
22 pub fn hours(&self) -> Option<Hours> {
23 self.hours
24 }
25
26 pub fn divider<Tz: chrono::TimeZone>(&self, timestamp: DateTime<Tz>) -> u8 {
27 if let Some(hours) = self.hours()
28 && hours.matches(timestamp)
29 {
30 self.divider
31 } else {
32 1
33 }
34 }
35
36 pub fn multiplier<Tz: chrono::TimeZone>(&self, timestamp: DateTime<Tz>) -> f64 {
37 1. / self.divider(timestamp) as f64
38 }
39
40 pub fn divided_percentage(&self) -> u8 {
41 ((1.0 / self.divider as f64) * 100.) as u8
42 }
43}
44
45#[derive(Debug, Clone, Serialize)]
46#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
47pub enum PowerTariff {
48 Unverified,
49 NotImplemented,
50 Periods {
51 method: TariffCalculationMethod,
52 power_divide: Option<PowerDivide>,
54 periods: CostPeriods,
55 },
56}
57
58impl PowerTariff {
59 pub const fn new(method: TariffCalculationMethod, periods: CostPeriods) -> Self {
60 Self::Periods {
61 method,
62 periods,
63 power_divide: None,
64 }
65 }
66
67 pub const fn add_power_divide(self, power_divide: PowerDivide) -> Self {
68 match self {
69 Self::Unverified => unimplemented!(),
70 Self::NotImplemented => unimplemented!(),
71 Self::Periods {
72 method,
73 power_divide: _,
74 periods,
75 } => Self::Periods {
76 method,
77 power_divide: Some(power_divide),
78 periods,
79 },
80 }
81 }
82
83 pub const fn is_unverified(&self) -> bool {
84 matches!(self, Self::Unverified)
85 }
86
87 pub fn simplified(
88 &self,
89 operator: &GridOperator,
90 fuse_size: u16,
91 yearly_consumption: u32,
92 language: Language,
93 ) -> PowerTariffSimplified {
94 PowerTariffSimplified::new(operator, self, fuse_size, yearly_consumption, language)
95 }
96
97 pub fn periods(
98 &self,
99 averages: Vec<PowerAverage<Actual>>,
100 current_power_average: Option<PartialPowerAverage>,
101 ) -> PowerTariffMatches {
102 match self {
103 PowerTariff::Unverified => PowerTariffMatches::new_dummy(),
104 PowerTariff::NotImplemented => PowerTariffMatches::new_dummy(),
105 PowerTariff::Periods {
106 method,
107 periods,
108 power_divide,
109 } => PowerTariffMatches::new(
110 *method,
111 *power_divide,
112 *periods,
113 &averages
114 .iter()
115 .map(|a| a.into_virtual(*power_divide))
116 .collect::<Vec<_>>(),
117 current_power_average,
118 ),
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, Serialize)]
125#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
126pub enum TariffCalculationMethod {
127 AverageDays(u8),
129 AverageHours(u8),
131}
132
133#[derive(Debug, Clone, Serialize)]
135#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
136pub enum PowerTariffSimplified {
137 Unverified,
138 NotImplemented,
139 Periods {
140 method: TariffCalculationMethod,
141 power_divide: Option<PowerDivide>,
143 periods: CostPeriodsSimple,
144 info: Option<String>,
145 },
146}
147
148impl PowerTariffSimplified {
149 fn new(
150 operator: &GridOperator,
151 tariff: &PowerTariff,
152 fuse_size: u16,
153 yearly_consumption: u32,
154 language: Language,
155 ) -> Self {
156 match *tariff {
157 PowerTariff::Unverified => PowerTariffSimplified::Unverified,
158 PowerTariff::NotImplemented => PowerTariffSimplified::NotImplemented,
159 PowerTariff::Periods {
160 method,
161 periods,
162 power_divide,
163 } => PowerTariffSimplified::Periods {
164 method,
165 power_divide,
166 periods: CostPeriodsSimple::new(periods, fuse_size, yearly_consumption, language),
167 info: Self::info(operator, tariff, language),
168 },
169 }
170 }
171
172 fn info(operator: &GridOperator, tariff: &PowerTariff, language: Language) -> Option<String> {
173 match language {
174 Language::En => {
175 let (method, power_divide) = match tariff {
176 PowerTariff::Unverified | PowerTariff::NotImplemented => return None,
177 PowerTariff::Periods {
178 method,
179 power_divide,
180 ..
181 } => (method, power_divide),
182 };
183
184 let mut s = format!(
185 "{} calculates its power tariff based on {} during the month.",
186 operator.name(),
187 match method {
188 TariffCalculationMethod::AverageDays(1)
189 | TariffCalculationMethod::AverageHours(1) =>
190 "the highest power peak".to_string(),
191 TariffCalculationMethod::AverageDays(n) =>
192 format!("the average of the {n} highest daily power peaks"),
193 TariffCalculationMethod::AverageHours(n) =>
194 format!("the average of the {n} highest power peaks"),
195 }
196 );
197
198 if let Some(power_divide) = power_divide {
199 let hours = power_divide
200 .hours()
201 .expect("hours to be defined on pricelist");
202
203 s.push_str(&format!(
204 " Between {}:00 and {}:59, only {}% of the power peak is counted.",
205 hours.from(),
206 hours.to_inclusive(),
207 power_divide.divided_percentage()
208 ));
209 }
210 Some(s)
211 }
212 Language::Sv => {
213 let (method, power_divide) = match tariff {
214 PowerTariff::Unverified | PowerTariff::NotImplemented => return None,
215 PowerTariff::Periods {
216 method,
217 power_divide,
218 ..
219 } => (method, power_divide),
220 };
221 let mut s = format!(
222 "{} räknar ut sin effektavgift utifrån {} under månaden.",
223 operator.name(),
224 match method {
225 TariffCalculationMethod::AverageDays(1)
226 | TariffCalculationMethod::AverageHours(1) =>
227 "den högsta effekttoppen".to_string(),
228 TariffCalculationMethod::AverageDays(n) =>
229 format!("snittet av de {n} högsta dygnseffekttopparna"),
230 TariffCalculationMethod::AverageHours(n) =>
231 format!("snittet av de {n} högsta effekttopparna"),
232 }
233 );
234 if let Some(power_divide) = power_divide {
235 let hours = power_divide
236 .hours()
237 .expect("hours to be defined on pricelist");
238 s.push_str(&format!(
239 " Mellan kl {}:00 och {}:59 så räknas bara {}% av effekttoppen.",
240 hours.from(),
241 hours.to_inclusive(),
242 power_divide.divided_percentage()
243 ));
244 }
245 Some(s)
246 }
247 }
248 }
249}