1use crate::entity_link::EntityLink;
30use crate::schema::environment_metrics::EnvironmentMetrics;
31use crate::schema::sensor::Sensor as SchemaSensor;
32use crate::Error;
33use nv_redfish_core::Bmc;
34use nv_redfish_core::NavProperty;
35use nv_redfish_core::ODataId;
36
37#[macro_export(local_inner_macros)]
51macro_rules! extract_sensor_uris {
52 ($metrics:expr, $(single: $single_field:ident),* $(, vec: $vec_field:ident)* $(,)?) => {{
53 let mut uris = Vec::new();
54
55 $(
56 if let Some(Some(uri)) = $metrics.$single_field.as_ref()
57 .and_then(|f| f.data_source_uri.as_ref()) {
58 uris.push(uri.clone());
59 }
60 )*
61
62 $(
63 if let Some(items) = &$metrics.$vec_field {
64 for item in items {
65 if let Some(Some(uri)) = item.data_source_uri.as_ref() {
66 uris.push(uri.clone());
67 }
68 }
69 }
70 )*
71
72 $crate::sensor::collect_sensors(uris)
73 }};
74}
75pub type SensorLink<B> = EntityLink<B, SchemaSensor>;
77
78pub(crate) fn collect_sensors(
80 uris: impl IntoIterator<Item = String>,
81) -> Vec<NavProperty<SchemaSensor>> {
82 uris.into_iter()
83 .map(|uri| NavProperty::<SchemaSensor>::new_reference(ODataId::from(uri)))
84 .collect()
85}
86
87pub(crate) async fn extract_environment_sensors<B: Bmc>(
89 metrics_ref: &NavProperty<EnvironmentMetrics>,
90 bmc: &B,
91) -> Result<Vec<NavProperty<SchemaSensor>>, Error<B>> {
92 metrics_ref
93 .get(bmc)
94 .await
95 .map(|m| {
96 extract_sensor_uris!(m,
97 single: temperature_celsius,
98 single: humidity_percent,
99 single: power_watts,
100 single: energyk_wh,
101 single: power_load_percent,
102 single: dew_point_celsius,
103 single: absolute_humidity,
104 single: energy_joules,
105 single: ambient_temperature_celsius,
106 single: voltage,
107 single: current_amps,
108 vec: fan_speeds_percent
109 )
110 })
111 .map_err(Error::Bmc)
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::schema::control::ControlExcerptSingle;
117 use crate::schema::environment_metrics::EnvironmentMetrics;
118
119 #[test]
120 fn power_limit_watts_uses_control_excerpt() -> Result<(), Box<dyn std::error::Error>> {
121 let metrics: EnvironmentMetrics = serde_json::from_str(
122 r##"{
123 "@odata.id": "/redfish/v1/Chassis/1/EnvironmentMetrics",
124 "@odata.type": "#EnvironmentMetrics.v1_1_0.EnvironmentMetrics",
125 "Id": "EnvironmentMetrics",
126 "Name": "Environment Metrics",
127 "PowerLimitWatts": {
128 "DataSourceUri": "/redfish/v1/Chassis/1/Controls/PowerLimit",
129 "SetPoint": 700
130 }
131 }"##,
132 )?;
133 let Some(power_limit): Option<ControlExcerptSingle> = metrics.power_limit_watts else {
134 return Err(std::io::Error::new(
135 std::io::ErrorKind::InvalidData,
136 "missing PowerLimitWatts",
137 )
138 .into());
139 };
140
141 assert_eq!(
142 power_limit
143 .data_source_uri
144 .as_ref()
145 .and_then(Option::as_deref),
146 Some("/redfish/v1/Chassis/1/Controls/PowerLimit")
147 );
148 assert_eq!(power_limit.set_point, Some(Some(700.0)));
149
150 Ok(())
151 }
152}