use crate::entity_link::EntityLink;
use crate::schema::environment_metrics::EnvironmentMetrics;
use crate::schema::sensor::Sensor as SchemaSensor;
use crate::Error;
use nv_redfish_core::Bmc;
use nv_redfish_core::NavProperty;
use nv_redfish_core::ODataId;
#[macro_export(local_inner_macros)]
macro_rules! extract_sensor_uris {
($metrics:expr, $(single: $single_field:ident),* $(, vec: $vec_field:ident)* $(,)?) => {{
let mut uris = Vec::new();
$(
if let Some(Some(uri)) = $metrics.$single_field.as_ref()
.and_then(|f| f.data_source_uri.as_ref()) {
uris.push(uri.clone());
}
)*
$(
if let Some(items) = &$metrics.$vec_field {
for item in items {
if let Some(Some(uri)) = item.data_source_uri.as_ref() {
uris.push(uri.clone());
}
}
}
)*
$crate::sensor::collect_sensors(uris)
}};
}
pub type SensorLink<B> = EntityLink<B, SchemaSensor>;
pub(crate) fn collect_sensors(
uris: impl IntoIterator<Item = String>,
) -> Vec<NavProperty<SchemaSensor>> {
uris.into_iter()
.map(|uri| NavProperty::<SchemaSensor>::new_reference(ODataId::from(uri)))
.collect()
}
pub(crate) async fn extract_environment_sensors<B: Bmc>(
metrics_ref: &NavProperty<EnvironmentMetrics>,
bmc: &B,
) -> Result<Vec<NavProperty<SchemaSensor>>, Error<B>> {
metrics_ref
.get(bmc)
.await
.map(|m| {
extract_sensor_uris!(m,
single: temperature_celsius,
single: humidity_percent,
single: power_watts,
single: energyk_wh,
single: power_load_percent,
single: dew_point_celsius,
single: absolute_humidity,
single: energy_joules,
single: ambient_temperature_celsius,
single: voltage,
single: current_amps,
vec: fan_speeds_percent
)
})
.map_err(Error::Bmc)
}
#[cfg(test)]
mod tests {
use crate::schema::control::ControlExcerptSingle;
use crate::schema::environment_metrics::EnvironmentMetrics;
#[test]
fn power_limit_watts_uses_control_excerpt() -> Result<(), Box<dyn std::error::Error>> {
let metrics: EnvironmentMetrics = serde_json::from_str(
r##"{
"@odata.id": "/redfish/v1/Chassis/1/EnvironmentMetrics",
"@odata.type": "#EnvironmentMetrics.v1_1_0.EnvironmentMetrics",
"Id": "EnvironmentMetrics",
"Name": "Environment Metrics",
"PowerLimitWatts": {
"DataSourceUri": "/redfish/v1/Chassis/1/Controls/PowerLimit",
"SetPoint": 700
}
}"##,
)?;
let Some(power_limit): Option<ControlExcerptSingle> = metrics.power_limit_watts else {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"missing PowerLimitWatts",
)
.into());
};
assert_eq!(
power_limit
.data_source_uri
.as_ref()
.and_then(Option::as_deref),
Some("/redfish/v1/Chassis/1/Controls/PowerLimit")
);
assert_eq!(power_limit.set_point, Some(Some(700.0)));
Ok(())
}
}