use crate::actor::State;
use crate::message::Message;
use std::fmt;
use time::OffsetDateTime;
type OperatorResult<T> = std::result::Result<T, OperatorError>;
#[derive(Debug, Clone)]
pub struct OperatorError {
reason: String,
}
impl fmt::Display for OperatorError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unsuccessful operation: {}", self.reason)
}
}
pub trait Operator {
fn apply(
state: &State<f64>,
idx: i32,
value: f64,
datetime: OffsetDateTime,
) -> OperatorResult<f64>;
}
pub struct GuageOperator {}
impl Operator for GuageOperator {
fn apply(_: &State<f64>, _: i32, value: f64, _: OffsetDateTime) -> OperatorResult<f64> {
Ok(value)
}
}
pub struct AccumOperator {}
impl Operator for AccumOperator {
fn apply(state: &State<f64>, idx: i32, value: f64, _: OffsetDateTime) -> OperatorResult<f64> {
state.get(&idx).map_or_else(
|| {
Err(OperatorError {
reason: String::from("idx invalid"),
})
},
|old_val| {
let new_val = old_val + value;
println!("oldval: {old_val}");
println!("observation: {value}");
println!("newval: {new_val}");
Ok(new_val)
},
)
}
}
pub trait Gene {
fn apply_operators(
&self,
state: State<f64>,
update: crate::genes::Message,
) -> OperatorResult<State<f64>>;
fn get_time_scope(&self) -> &TimeScope;
}
pub struct GuageAndAccumGene {
pub guage_first_idx: i32,
pub guage_slots: i32,
pub accumulator_first_idx: i32,
pub accumulator_slots: i32,
pub time_scope: TimeScope,
}
impl Gene for GuageAndAccumGene {
fn get_time_scope(&self) -> &TimeScope {
&self.time_scope
}
fn apply_operators(
&self,
mut state: State<f64>,
update: Message,
) -> OperatorResult<State<f64>> {
if let Message::Update {
path: _,
datetime,
values,
} = update
{
for &idx in values.keys() {
if let Some(in_val) = values.get(&idx) {
match idx {
i if (self.guage_first_idx..self.guage_first_idx + self.guage_slots)
.contains(&i) =>
{
match GuageOperator::apply(&state, i, *in_val, datetime) {
Ok(new_val) => {
state.insert(i, new_val);
}
Err(e) => return Err(e),
}
}
i if (self.accumulator_first_idx
..self.accumulator_first_idx + self.accumulator_slots)
.contains(&i) =>
{
match AccumOperator::apply(&state, i, *in_val, datetime) {
Ok(new_val) => {
state.insert(i, new_val);
}
Err(e) => return Err(e),
}
}
i => {
return Err(OperatorError {
reason: format!("unsupported idx: {i}"),
})
}
}
} else {
return Err(OperatorError {
reason: String::from("cannot read input value"),
});
}
}
}
Ok(state)
}
}
impl Default for GuageAndAccumGene {
fn default() -> Self {
Self {
guage_first_idx: 0,
guage_slots: 100,
accumulator_first_idx: 100,
accumulator_slots: 100,
time_scope: TimeScope::Forever,
}
}
}
#[derive(Debug, Clone)]
pub enum TimeScope {
Forever,
Year,
Month,
Day,
HalfDay,
QuarterDay,
Hour,
QuarterHour,
TenMinutes,
Minute,
}