use serde::{Deserialize, Serialize}; use async_trait::async_trait; use crate::services::{ analytic_service::types::HSR, metric_service::MetricService, segments_service::SegmentsService, }; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct PatternConfig { pub correlation_score: f32, pub anti_correlation_score: f32, pub model_score: f32, pub threshold_score: f32, } impl Default for PatternConfig { fn default() -> Self { PatternConfig { correlation_score: 0.3, anti_correlation_score: 0.1, model_score: 0.8, threshold_score: 1.0, } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AnomalyConfig { pub alpha: f64, pub confidence: f64, pub seasonality: u64, // step in seconds, can be zero pub seasonality_iterations: u64, } impl Default for AnomalyConfig { fn default() -> Self { AnomalyConfig { alpha: 0.5, confidence: 10.0, seasonality: 60 * 60, seasonality_iterations: 3, } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ThresholdConfig { pub threshold: f64, } impl Default for ThresholdConfig { fn default() -> Self { ThresholdConfig { threshold: 0.5 } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub enum AnalyticUnitConfig { Threshold(ThresholdConfig), Pattern(PatternConfig), Anomaly(AnomalyConfig), } impl AnalyticUnitConfig { pub fn get_default_by_id(id: &String) -> AnalyticUnitConfig { let iid = id.as_str(); match iid { "1" => AnalyticUnitConfig::Threshold(Default::default()), "2" => AnalyticUnitConfig::Pattern(Default::default()), "3" => AnalyticUnitConfig::Anomaly(Default::default()), _ => panic!("bad id for getting get_default_by_id"), } } pub fn patch_needs_learning(&self, patch: &PatchConfig) -> bool { // TODO: maybe use type id's to optimise code match patch { PatchConfig::Pattern(tcfg) => match self.clone() { AnalyticUnitConfig::Pattern(_) => { return false; } _ => return true, }, PatchConfig::Anomaly(tcfg) => match self.clone() { AnalyticUnitConfig::Anomaly(scfg) => { if tcfg.is_some() { let t = tcfg.as_ref().unwrap(); let mut need_learning = t.seasonality != scfg.seasonality; need_learning |= t.seasonality_iterations != scfg.seasonality_iterations; return need_learning; } else { return false; } } _ => { return true; } }, PatchConfig::Threshold(tcfg) => match self.clone() { AnalyticUnitConfig::Threshold(_) => { return false; } _ => { return true; } }, } } // TODO: maybe this method depricated // return true if need needs relearning pub fn patch(&self, patch: PatchConfig) -> (AnalyticUnitConfig, bool) { match patch { PatchConfig::Pattern(tcfg) => match self.clone() { AnalyticUnitConfig::Pattern(_) => { if tcfg.is_some() { return (AnalyticUnitConfig::Pattern(tcfg.unwrap()), false); } else { // TODO: it should be extraced from db return (AnalyticUnitConfig::Pattern(Default::default()), false); } } _ => { if tcfg.is_some() { return (AnalyticUnitConfig::Pattern(tcfg.unwrap()), true); } else { return (AnalyticUnitConfig::Pattern(Default::default()), true); } } }, PatchConfig::Anomaly(tcfg) => match self.clone() { AnalyticUnitConfig::Anomaly(scfg) => { if tcfg.is_some() { let t = tcfg.as_ref().unwrap(); let mut need_learning = t.seasonality != scfg.seasonality; need_learning |= t.seasonality_iterations != scfg.seasonality_iterations; return (AnalyticUnitConfig::Anomaly(tcfg.unwrap()), need_learning); } else { return (AnalyticUnitConfig::Anomaly(Default::default()), false); } } _ => { if tcfg.is_some() { return (AnalyticUnitConfig::Anomaly(tcfg.unwrap()), true); } else { return (AnalyticUnitConfig::Anomaly(Default::default()), true); } } }, PatchConfig::Threshold(tcfg) => match self.clone() { AnalyticUnitConfig::Threshold(_) => { if tcfg.is_some() { return (AnalyticUnitConfig::Threshold(tcfg.unwrap()), false); } else { return (AnalyticUnitConfig::Threshold(Default::default()), false); } } _ => { if tcfg.is_some() { return (AnalyticUnitConfig::Threshold(tcfg.unwrap()), true); } else { return (AnalyticUnitConfig::Threshold(Default::default()), true); } } }, } } } pub enum LearningResult { Finished, FinishedEmpty, } #[async_trait] pub trait AnalyticUnit { fn get_id(&self) -> String; fn get_detection_window(&self) -> u64; async fn learn( &mut self, ms: MetricService, ss: SegmentsService, ) -> anyhow::Result; async fn detect( &self, ms: MetricService, from: u64, to: u64, ) -> anyhow::Result>; fn set_config(&mut self, c: AnalyticUnitConfig); async fn get_hsr(&self, ms: MetricService, from: u64, to: u64) -> anyhow::Result; } #[derive(Deserialize, Serialize, Debug)] pub enum PatchConfig { Pattern(Option), Threshold(Option), Anomaly(Option), } impl PatchConfig { pub fn get_type_id(&self) -> String { match &self { PatchConfig::Threshold(_) => "1".to_string(), PatchConfig::Pattern(_) => "2".to_string(), PatchConfig::Anomaly(_) => "3".to_string(), } } pub fn get_new_config(&self) -> AnalyticUnitConfig { match &self { PatchConfig::Threshold(cfg) => { AnalyticUnitConfig::Threshold(cfg.as_ref().unwrap().clone()) } PatchConfig::Pattern(cfg) => AnalyticUnitConfig::Pattern(cfg.as_ref().unwrap().clone()), PatchConfig::Anomaly(cfg) => AnalyticUnitConfig::Anomaly(cfg.as_ref().unwrap().clone()), } } }