diff --git a/src/handler/control.rs b/src/handler/control.rs index d19c350..6909b57 100644 --- a/src/handler/control.rs +++ b/src/handler/control.rs @@ -26,11 +26,19 @@ pub struct GetUnitListQuery { pub pagination: PaginationParams, } +#[derive(serde::Serialize)] +pub struct UnitEquipmentItem { + #[serde(flatten)] + pub equipment: crate::model::Equipment, + pub role_points: Vec, +} + #[derive(serde::Serialize)] pub struct UnitWithRuntime { #[serde(flatten)] pub unit: crate::model::ControlUnit, pub runtime: Option, + pub equipments: Vec, } pub async fn get_unit_list( @@ -40,7 +48,7 @@ pub async fn get_unit_list( query.validate()?; let total = crate::service::get_units_count(&state.pool, query.keyword.as_deref()).await?; - let data = crate::service::get_units_paginated( + let units = crate::service::get_units_paginated( &state.pool, query.keyword.as_deref(), query.pagination.page_size, @@ -49,11 +57,54 @@ pub async fn get_unit_list( .await?; let all_runtimes = state.control_runtime.get_all().await; - let data = data + + let unit_ids: Vec = units.iter().map(|u| u.id).collect(); + let all_equipments = + crate::service::get_equipment_by_unit_ids(&state.pool, &unit_ids).await?; + + let eq_ids: Vec = all_equipments.iter().map(|e| e.id).collect(); + let role_point_rows = + crate::service::get_signal_role_points_batch(&state.pool, &eq_ids).await?; + + let monitor_guard = state + .connection_manager + .get_point_monitor_data_read_guard() + .await; + + let mut role_points_map: std::collections::HashMap< + Uuid, + Vec, + > = std::collections::HashMap::new(); + for rp in role_point_rows { + role_points_map + .entry(rp.equipment_id) + .or_default() + .push(crate::handler::equipment::SignalRolePoint { + point_id: rp.point_id, + signal_role: rp.signal_role, + point_monitor: monitor_guard.get(&rp.point_id).cloned(), + }); + } + drop(monitor_guard); + + let mut equipments_by_unit: std::collections::HashMap> = + std::collections::HashMap::new(); + for eq in all_equipments { + let role_points = role_points_map.remove(&eq.id).unwrap_or_default(); + if let Some(unit_id) = eq.unit_id { + equipments_by_unit + .entry(unit_id) + .or_default() + .push(UnitEquipmentItem { equipment: eq, role_points }); + } + } + + let data = units .into_iter() .map(|unit| { let runtime = all_runtimes.get(&unit.id).cloned(); - UnitWithRuntime { unit, runtime } + let equipments = equipments_by_unit.remove(&unit.id).unwrap_or_default(); + UnitWithRuntime { unit, runtime, equipments } }) .collect::>(); @@ -138,7 +189,41 @@ pub async fn get_unit( .await? .ok_or_else(|| ApiErr::NotFound("Unit not found".to_string(), None))?; let runtime = state.control_runtime.get(unit_id).await; - Ok(Json(UnitWithRuntime { unit, runtime })) + + let all_equipments = + crate::service::get_equipment_by_unit_id(&state.pool, unit_id).await?; + let eq_ids: Vec = all_equipments.iter().map(|e| e.id).collect(); + let role_point_rows = + crate::service::get_signal_role_points_batch(&state.pool, &eq_ids).await?; + let monitor_guard = state + .connection_manager + .get_point_monitor_data_read_guard() + .await; + let mut role_points_map: std::collections::HashMap< + Uuid, + Vec, + > = std::collections::HashMap::new(); + for rp in role_point_rows { + role_points_map + .entry(rp.equipment_id) + .or_default() + .push(crate::handler::equipment::SignalRolePoint { + point_id: rp.point_id, + signal_role: rp.signal_role, + point_monitor: monitor_guard.get(&rp.point_id).cloned(), + }); + } + drop(monitor_guard); + + let equipments = all_equipments + .into_iter() + .map(|eq| { + let role_points = role_points_map.remove(&eq.id).unwrap_or_default(); + UnitEquipmentItem { equipment: eq, role_points } + }) + .collect(); + + Ok(Json(UnitWithRuntime { unit, runtime, equipments })) } #[derive(serde::Serialize)] diff --git a/src/service/control.rs b/src/service/control.rs index fee1d4c..b6d549f 100644 --- a/src/service/control.rs +++ b/src/service/control.rs @@ -316,6 +316,21 @@ pub async fn get_all_enabled_units(pool: &PgPool) -> Result, sq .await } +pub async fn get_equipment_by_unit_ids( + pool: &PgPool, + unit_ids: &[Uuid], +) -> Result, sqlx::Error> { + if unit_ids.is_empty() { + return Ok(vec![]); + } + sqlx::query_as::<_, crate::model::Equipment>( + r#"SELECT * FROM equipment WHERE unit_id = ANY($1) ORDER BY unit_id, created_at"#, + ) + .bind(unit_ids) + .fetch_all(pool) + .await +} + pub async fn get_equipment_by_unit_id( pool: &PgPool, unit_id: Uuid,