Skip to main content

nv_redfish/
sensor.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Sensor abstraction for Redfish entities.
17//!
18//! This module provides a unified interface for accessing sensor data from
19//! Redfish entities that support modern sensor links. The `HasSensors` trait
20//! is implemented by entities that have associated sensors, and provides access
21//! to a `Sensor` handle for sensor data retrieval.
22//!
23//! # Modern vs Legacy Approach
24//!
25//! This module supports the modern Redfish approach where entities have direct
26//! links to their sensors. For legacy BMCs that only expose sensor data through
27//! `Chassis/Power` and `Chassis/Thermal`, use those explicit endpoints instead.
28
29use crate::schema::redfish::environment_metrics::EnvironmentMetrics;
30use crate::schema::redfish::sensor::Sensor as SchemaSensor;
31use crate::Error;
32use crate::NvBmc;
33use nv_redfish_core::Bmc;
34use nv_redfish_core::NavProperty;
35use nv_redfish_core::ODataId;
36use std::sync::Arc;
37
38/// Extracts sensor URIs from metric fields and creates sensor navigation properties.
39///
40/// Handles both single `Option<SensorExcerpt*>` and `Option<Vec<SensorExcerpt*>>` fields.
41/// All `single:` fields must come before `vec:` fields.
42///
43/// # Example
44/// ```ignore
45/// extract_sensor_uris!(metrics,
46///     single: temperature,
47///     single: voltage,
48///     vec: fan_speeds
49/// )
50/// ```
51#[macro_export(local_inner_macros)]
52macro_rules! extract_sensor_uris {
53    ($metrics:expr, $(single: $single_field:ident),* $(, vec: $vec_field:ident)* $(,)?) => {{
54        let mut uris = Vec::new();
55
56        $(
57            if let Some(Some(uri)) = $metrics.$single_field.as_ref()
58                .and_then(|f| f.data_source_uri.as_ref()) {
59                uris.push(uri.clone());
60            }
61        )*
62
63        $(
64            if let Some(items) = &$metrics.$vec_field {
65                for item in items {
66                    if let Some(Some(uri)) = item.data_source_uri.as_ref() {
67                        uris.push(uri.clone());
68                    }
69                }
70            }
71        )*
72
73        $crate::sensor::collect_sensors(uris)
74    }};
75}
76
77/// Handle for accessing sensor.
78///
79/// This struct provides methods to fetch sensor data from the BMC.
80/// call to [`fetch`](Self::fetch).
81pub struct SensorRef<B: Bmc> {
82    bmc: NvBmc<B>,
83    sensor_ref: NavProperty<SchemaSensor>,
84}
85
86impl<B: Bmc> SensorRef<B> {
87    /// Create a new sensor handle.
88    ///
89    /// # Arguments
90    ///
91    /// * `sensor_ref` - Navigation properties pointing to sensor
92    /// * `bmc` - BMC client for fetching sensor data
93    #[must_use]
94    pub(crate) const fn new(bmc: NvBmc<B>, sensor_ref: NavProperty<SchemaSensor>) -> Self {
95        Self { bmc, sensor_ref }
96    }
97
98    /// Refresh sensor data from the BMC.
99    ///
100    /// Fetches current sensor readings from the BMC.
101    /// This method performs network I/O and may take time to complete.
102    ///
103    /// # Errors
104    ///
105    /// Returns an error if sensor fetch fails.
106    pub async fn fetch(&self) -> Result<Arc<SchemaSensor>, Error<B>> {
107        let sensor = self
108            .sensor_ref
109            .get(self.bmc.as_ref())
110            .await
111            .map_err(Error::Bmc)?;
112
113        Ok(sensor)
114    }
115
116    /// `OData` identifier of the `NavProperty<Sensor>` in Redfish.
117    ///
118    /// Typically `/redfish/v1/{Chassis}/Sensors/{ID}`.
119    #[must_use]
120    pub fn odata_id(&self) -> &ODataId {
121        self.sensor_ref.id()
122    }
123}
124
125/// Collect sensor refs from URIs
126pub(crate) fn collect_sensors(
127    uris: impl IntoIterator<Item = String>,
128) -> Vec<NavProperty<SchemaSensor>> {
129    uris.into_iter()
130        .map(|uri| NavProperty::<SchemaSensor>::new_reference(ODataId::from(uri)))
131        .collect()
132}
133
134/// Helper function to extract enviroment metrics
135pub(crate) async fn extract_environment_sensors<B: Bmc>(
136    metrics_ref: &NavProperty<EnvironmentMetrics>,
137    bmc: &B,
138) -> Result<Vec<NavProperty<SchemaSensor>>, Error<B>> {
139    metrics_ref
140        .get(bmc)
141        .await
142        .map(|m| {
143            extract_sensor_uris!(m,
144                single: temperature_celsius,
145                single: humidity_percent,
146                single: power_watts,
147                single: energyk_wh,
148                single: power_load_percent,
149                single: dew_point_celsius,
150                single: absolute_humidity,
151                single: energy_joules,
152                single: ambient_temperature_celsius,
153                single: voltage,
154                single: current_amps,
155                vec: fan_speeds_percent
156            )
157        })
158        .map_err(Error::Bmc)
159}