A library to create formulas over streamed data, primarily used for calculating and processing values within microgrid applications.
The FormulaEngine
can only work with resampled component data streams. It has been designed to work with the following libraries:
- frequenz-resampling-rs - A resampling library, which sends
None
values when data is missing. See Handling Nulls and Missing Values. - frequenz-microgrid-component-graph-rs - A component graph library, for generating formulas.
The FormulaEngine
can be created from a string formula using the try_new
method. Formulas can contain component placeholders, represented by #
followed by a number. To calculate the formula, provide an iterator of Option
values where:
None
represents a missing valueSome(value)
represents a value
The result of the calculation will be an Option
value.
use frequenz_formula_engine::{FormulaEngine, FormulaError};
fn main() -> Result<(), FormulaError> {
let fe = FormulaEngine::try_new("#0 + #1")?;
assert_eq!(fe.calculate(&[Some(1.0), Some(2.0)])?, Some(3.0));
Ok(())
}
When dealing with missing data, use None
to indicate a missing value. If the formula requires a value for a placeholder but it is missing, the result will also be None
unless a fallback function like COALESCE
is used.
Example:
COALESCE(#1, 0) // If #1 is None, it will return 0
The FormulaEngine may return errors for invalid formulas, incorrect argument counts, or division by zero. These are returned as a FormulaError. Handle them gracefully for robust applications.
match FormulaEngine::try_new("#0 / #1") {
Ok(fe) => match fe.calculate(&[Some(10.0), Some(0.0)]) {
Ok(result) => println!("Result: {:?}", result),
Err(e) => println!("Calculation error: {:?}", e),
},
Err(e) => println!("Invalid formula: {:?}", e),
}
The formula engine supports simple arithmetic expressions that combine numbers, references to components, mathematical operators, and basic functions.
You can write integer or decimal numbers directly in formulas:
42
3.14
0.001
Microgrid electrical component or sensor references are written as # followed by one or more digits:
#1 // Refers to component 1
#42 // Refers to component 42
The following standard arithmetic operators are supported:
- Addition:
+
- Subtraction:
-
(also supports unary minus, e.g.,-5
) - Multiplication:
*
- Division:
/
Expressions follow standard precedence rules (multiplication/division before addition/subtraction). Use parentheses to override precedence as needed:
#1 + 3 * #2 // Multiplies #2 by 3, then adds #1
(#1 + 3) * #2 // Adds #1 and 3, then multiplies the result by #2
Formulas support these functions that operate on a comma-separated list of expressions:
COALESCE(a, b, ...)
— Returns the first non-null value from the listMIN(a, b, ...)
— Returns the smallest valueMAX(a, b, ...)
— Returns the largest value
Examples:
COALESCE(#3, 0) // Returns #3 if it's not null, otherwise 0
MIN(#1, #2, 100) // Returns the smaller value of component #1 and #2, but at most the value of 100
MAX(#1 + 2, #4 * 5) // Evaluates both expressions, returns the larger
Whitespace is ignored and can be used freely to improve readability:
( #1 + 4 ) * ( #2 - 1 )
Here are some additional examples of the formula engine syntax:
#1 + 5
-#2 / 3.0
(#1 + #2) * 0.5
COALESCE(#5, #6, 0)
MAX(0, #3 - 100)
To add the library to your project, include the following in your Cargo.toml:
[dependencies]
frequenz-microgrid-formula-engine = "0.1"