|
3 | 3 |
|
4 | 4 | //! Formula module for the logical meter. |
5 | 5 |
|
| 6 | +use frequenz_microgrid_component_graph::Formula as _; |
6 | 7 | mod aggregation_formula; |
7 | 8 | mod coalesce_formula; |
8 | 9 | pub(crate) mod graph_formula_provider; |
9 | 10 | pub use aggregation_formula::AggregationFormula; |
10 | 11 | pub use coalesce_formula::CoalesceFormula; |
11 | 12 |
|
12 | | -use crate::{Error, Sample}; |
13 | | -use tokio::sync::broadcast; |
| 13 | +use crate::{Error, Sample, proto::common::v1::metrics::Metric}; |
| 14 | +use tokio::sync::{broadcast, mpsc}; |
| 15 | + |
| 16 | +use super::logical_meter_actor; |
| 17 | + |
| 18 | +/// Connects logical meter formulas to the component graph formulas. |
| 19 | +pub(crate) trait GraphFormulaProvider: std::fmt::Display { |
| 20 | + type GraphFormulaType: frequenz_microgrid_component_graph::Formula; |
| 21 | +} |
14 | 22 |
|
15 | 23 | /// Defines a formula that can be subscribed to for receiving samples. |
16 | | -pub trait Formula: std::fmt::Display { |
| 24 | +pub(crate) trait FormulaSubscriber: std::fmt::Display { |
| 25 | + fn subscribe(&self) -> impl Future<Output = Result<broadcast::Receiver<Sample>, Error>> + Send; |
| 26 | +} |
| 27 | + |
| 28 | +/// Parameters for creating a logical meter formula. |
| 29 | +pub(super) struct FormulaParams<F: GraphFormulaProvider> { |
| 30 | + pub(super) formula: F::GraphFormulaType, |
| 31 | + pub(super) metric: Metric, |
| 32 | + pub(super) instructions_tx: mpsc::Sender<logical_meter_actor::Instruction>, |
| 33 | +} |
| 34 | + |
| 35 | +impl<F: GraphFormulaProvider> FormulaParams<F> { |
| 36 | + pub(super) fn new( |
| 37 | + formula: F::GraphFormulaType, |
| 38 | + metric: Metric, |
| 39 | + instructions_tx: mpsc::Sender<logical_meter_actor::Instruction>, |
| 40 | + ) -> Self { |
| 41 | + Self { |
| 42 | + formula, |
| 43 | + metric, |
| 44 | + instructions_tx, |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +/// A trait that defines generic formula operations. |
| 50 | +pub trait Formula: std::fmt::Display + Sized { |
| 51 | + fn coalesce(self, other: Self) -> Result<Self, Error>; |
| 52 | + fn min(self, other: Self) -> Result<Self, Error>; |
| 53 | + fn max(self, other: Self) -> Result<Self, Error>; |
17 | 54 | fn subscribe(&self) -> impl Future<Output = Result<broadcast::Receiver<Sample>, Error>> + Send; |
18 | 55 | } |
| 56 | + |
| 57 | +impl<T> Formula for T |
| 58 | +where |
| 59 | + T: FormulaSubscriber |
| 60 | + + GraphFormulaProvider |
| 61 | + + From<FormulaParams<T>> |
| 62 | + + Into<FormulaParams<T>> |
| 63 | + + std::fmt::Display, |
| 64 | +{ |
| 65 | + fn coalesce(self, other: Self) -> Result<Self, Error> { |
| 66 | + let mut params_self: FormulaParams<T> = self.into(); |
| 67 | + let params_other: FormulaParams<T> = other.into(); |
| 68 | + |
| 69 | + if params_self.metric != params_other.metric { |
| 70 | + return Err(Error::invalid_metric(format!( |
| 71 | + "Cannot coalesce formulas with different metrics: {} and {}", |
| 72 | + params_self.metric.as_str_name(), |
| 73 | + params_other.metric.as_str_name() |
| 74 | + ))); |
| 75 | + } |
| 76 | + params_self.formula = params_self.formula.coalesce(params_other.formula); |
| 77 | + Ok(params_self.into()) |
| 78 | + } |
| 79 | + |
| 80 | + fn min(self, other: Self) -> Result<Self, Error> { |
| 81 | + let mut params_self: FormulaParams<T> = self.into(); |
| 82 | + let params_other: FormulaParams<T> = other.into(); |
| 83 | + |
| 84 | + if params_self.metric != params_other.metric { |
| 85 | + return Err(Error::invalid_metric(format!( |
| 86 | + "Cannot take min of formulas with different metrics: {} and {}", |
| 87 | + params_self.metric.as_str_name(), |
| 88 | + params_other.metric.as_str_name() |
| 89 | + ))); |
| 90 | + } |
| 91 | + params_self.formula = params_self.formula.min(params_other.formula); |
| 92 | + Ok(params_self.into()) |
| 93 | + } |
| 94 | + |
| 95 | + fn max(self, other: Self) -> Result<Self, Error> { |
| 96 | + let mut params_self: FormulaParams<T> = self.into(); |
| 97 | + let params_other: FormulaParams<T> = other.into(); |
| 98 | + |
| 99 | + if params_self.metric != params_other.metric { |
| 100 | + return Err(Error::invalid_metric(format!( |
| 101 | + "Cannot take max of formulas with different metrics: {} and {}", |
| 102 | + params_self.metric.as_str_name(), |
| 103 | + params_other.metric.as_str_name() |
| 104 | + ))); |
| 105 | + } |
| 106 | + params_self.formula = params_self.formula.max(params_other.formula); |
| 107 | + Ok(params_self.into()) |
| 108 | + } |
| 109 | + |
| 110 | + fn subscribe(&self) -> impl Future<Output = Result<broadcast::Receiver<Sample>, Error>> + Send { |
| 111 | + <T as FormulaSubscriber>::subscribe(self) |
| 112 | + } |
| 113 | +} |
0 commit comments