use fathom_function::tracing; use pipeline_application::{ application::{ Application, IliComparisonOutput, MatchingCriteria, SurfaceLocationCriteria as SLC, }, serialization::{ serialize_meter, serialize_millimeter, serialize_orientation_min, serialize_percent, }, }; use uom::si::f64::{Angle, Length, Ratio}; use uuid::Uuid; #[fathom_function::function] async fn ili_compare_all(input: Input) -> Result { let mut output = Output::new(input.org_id, &input.project_id); let app = Application::new_from_compile_env(input.org_id, &input.project_id).unwrap(); for pipeline_id in input.pipeline_id { let result = app .ili_compare_all(pipeline_id, MatchingCriteria::from(&input.criteria)) .await .map_err(|err| { tracing::error!(%pipeline_id, ?err, "Error running comparison algorithm"); format!("{err:?}") })?; output.push_result(pipeline_id, result); } Ok(output) } #[derive(Debug, serde::Serialize, Default)] struct Output { // TODO: remove org_id, project_id, unique_pipeline_ids once function outputs from previous // nodes are all visible in downstream nodes (FTHM-11016). org_id: Uuid, unique_pipeline_ids: Vec, project_id: String, pipeline_ids: Vec, matched_ids: Vec, unmatched_ids: Vec, summary_ids: Vec, } impl Output { fn new(org_id: Uuid, project_id: impl ToString) -> Self { Self { org_id, project_id: project_id.to_string(), ..Default::default() } } fn push_result(&mut self, pipeline_id: Uuid, ili_comparison_result: Vec) { self.unique_pipeline_ids.push(pipeline_id); ili_comparison_result.into_iter().for_each(|res| { self.pipeline_ids.push(pipeline_id); self.matched_ids.push(res.matched_id); self.unmatched_ids.push(res.unmatched_id); self.summary_ids.push(res.summary_id); }); } } #[derive(Debug, serde::Deserialize)] struct Input { org_id: Uuid, project_id: String, pipeline_id: Vec, #[serde(flatten)] criteria: Criteria, } #[derive(Debug, serde::Deserialize)] struct Criteria { #[serde(with = "serialize_meter")] weld_location_threshold: Length, #[serde(with = "serialize_meter")] feature_location_threshold: Length, #[serde(with = "serialize_meter")] upstream_girth_threshold: Length, #[serde(with = "serialize_orientation_min")] orientation_threshold: Angle, #[serde(with = "serialize_percent")] minimum_depth_growth_threshold: Ratio, #[serde(with = "serialize_millimeter")] minimum_length_growth_threshold: Length, surface_location_criteria: SurfaceLocationCriteria, #[serde(with = "serialize_percent")] target_minimum_match_rate: Ratio, #[serde(default)] length_criteria_status: Status, } #[derive(Debug, Clone, Copy, serde::Deserialize)] #[serde(rename_all = "snake_case")] enum SurfaceLocationCriteria { Matching, Any, } impl From for SLC { fn from(value: SurfaceLocationCriteria) -> Self { match value { SurfaceLocationCriteria::Matching => Self::Matching, SurfaceLocationCriteria::Any => Self::Any, } } } #[derive(Debug, Default, Clone, Copy, serde::Deserialize)] #[serde(rename_all = "snake_case")] enum Status { #[default] Enabled, Disabled, } impl Status { fn is_disabled(&self) -> bool { matches!(self, Self::Disabled) } } impl From<&Criteria> for MatchingCriteria { fn from(value: &Criteria) -> Self { MatchingCriteria::default() .with_weld_location_threshold(value.weld_location_threshold) .with_feature_location_threshold(value.feature_location_threshold) .with_upstream_girth_threshold(value.upstream_girth_threshold) .with_orientation_threshold(value.orientation_threshold) .with_minimum_depth_growth_threshold(value.minimum_depth_growth_threshold) .with_minimum_length_growth_threshold(value.minimum_length_growth_threshold) .with_surface_location_criteria(value.surface_location_criteria) .with_target_minimum_match_rate(value.target_minimum_match_rate) .with_disable_length_criteria(value.length_criteria_status.is_disabled()) } }