1pub mod constants;
6pub mod data {
7 pub mod carbon_tracking;
8 pub mod cpu_tracking;
9}
10mod fs;
11pub mod macros;
12pub mod metrics;
13
14use std::path::PathBuf;
15
16pub use core_affinity;
17use log::debug;
18use metrics::{
19 carbon_equivalent::CarbonEquivalent, cpu::CpuCycleIntensity, flag::Flag, joule::Joule,
20 CarbonIntensity, Efficiency, Metric, MetricError,
21};
22pub use rand;
23use uom::si::f64::{Energy, Mass, MassPerEnergy};
24
25struct BatteryInfo {
26 name: String,
27 carbon_path: PathBuf,
28 energy_path: PathBuf,
29}
30
31pub async fn try_load_system_intensity() -> Result<CarbonIntensity, MetricError> {
34 CarbonIntensity::try_read_from_fs(constants::SYSTEM_INTENSITY_PATH).await
35}
36pub fn try_load_system_intensity_sync() -> Result<CarbonIntensity, MetricError> {
39 CarbonIntensity::try_read_from_fs_sync(constants::SYSTEM_INTENSITY_PATH)
40}
41
42pub async fn try_load_grid_intensity() -> Result<CarbonIntensity, MetricError> {
45 CarbonIntensity::try_read_from_fs(constants::GRID_INTENSITY_PATH).await
46}
47pub fn try_load_grid_intensity_sync() -> Result<CarbonIntensity, MetricError> {
50 CarbonIntensity::try_read_from_fs_sync(constants::GRID_INTENSITY_PATH)
51}
52
53pub async fn try_load_psu_efficiency() -> Result<Efficiency, MetricError> {
56 Efficiency::try_read_from_fs(constants::PSU_EFFICIENCY_PATH).await
57}
58pub fn try_load_psu_efficiency_sync() -> Result<Efficiency, MetricError> {
61 Efficiency::try_read_from_fs_sync(constants::PSU_EFFICIENCY_PATH)
62}
63
64pub async fn try_calculate_carbon_emission(energy: Energy) -> Result<Mass, MetricError> {
66 let carbon_intensity: MassPerEnergy = try_load_system_intensity().await?.get_value();
67 Ok(carbon_intensity * energy)
68}
69pub fn try_calculate_carbon_emission_sync(energy: Energy) -> Result<Mass, MetricError> {
71 let carbon_intensity: MassPerEnergy = try_load_system_intensity_sync()?.get_value();
72 Ok(carbon_intensity * energy)
73}
74
75pub async fn try_load_cpu_embodied_intensity() -> Result<Mass, MetricError> {
77 Ok(
78 CpuCycleIntensity::try_read_from_fs(constants::CPU_EMBODIED_PATH)
79 .await?
80 .get_value(),
81 )
82}
83pub fn try_load_cpu_embodied_intensity_sync() -> Result<Mass, MetricError> {
85 Ok(CpuCycleIntensity::try_read_from_fs_sync(constants::CPU_EMBODIED_PATH)?.get_value())
86}
87
88pub async fn try_load_battery_energy_intensity() -> Result<MassPerEnergy, MetricError> {
90 let batteries = load_battery_infos()?;
91
92 if batteries.is_empty() {
93 return Err(MetricError::Other(
94 "battery intensity".to_owned(),
95 "no batteries were found".to_owned(),
96 ));
97 }
98
99 let mut total_energy: Energy = Energy::new::<uom::si::energy::joule>(0.0);
100 let mut total_carbon: Mass = Mass::new::<uom::si::mass::gram>(0.0);
101 for battery in batteries {
102 let energy = Joule::try_read_from_path(&battery.energy_path)
103 .await?
104 .get_value();
105 let carbon = CarbonEquivalent::try_read_from_path(&battery.carbon_path)
106 .await?
107 .get_value();
108 debug!(
109 "Loaded tracker data for `{}`: Energy=`{:?}`, Carbon=`{:?}`.",
110 battery.name, energy, carbon
111 );
112 total_energy += energy;
113 total_carbon += carbon;
114 }
115
116 Ok(total_carbon / total_energy)
117}
118
119pub fn try_load_battery_energy_intensity_sync() -> Result<MassPerEnergy, MetricError> {
121 let batteries = load_battery_infos()?;
122 let mut total_energy: Energy = Energy::new::<uom::si::energy::joule>(0.0);
123 let mut total_carbon: Mass = Mass::new::<uom::si::mass::gram>(0.0);
124 for battery in batteries {
125 let energy = Joule::try_read_from_path_sync(&battery.energy_path)?.get_value();
126 let carbon = CarbonEquivalent::try_read_from_path_sync(&battery.carbon_path)?.get_value();
127 debug!(
128 "Loaded tracker data for `{}`: Energy=`{:?}`, Carbon=`{:?}`.",
129 battery.name, energy, carbon
130 );
131 total_energy += energy;
132 total_carbon += carbon;
133 }
134
135 Ok(total_carbon / total_energy)
136}
137
138fn load_battery_infos() -> Result<Vec<BatteryInfo>, MetricError> {
139 let battery_tracker_path = PathBuf::from(constants::BATTERY_TRACKER_PATH);
140 if !battery_tracker_path.exists() {
141 return Err(MetricError::Other(
142 "battery intensity".to_owned(),
143 "no battery tracker directory found".to_owned(),
144 ));
145 }
146 let paths = std::fs::read_dir(battery_tracker_path)?;
147 let mut battery_data = vec![];
148 for path in paths {
149 let path = path?.path();
150 let energy_path = path.join("energy");
151 let carbon_path = path.join("carbon");
152 if energy_path.exists()
153 && energy_path.is_file()
154 && carbon_path.exists()
155 && carbon_path.is_file()
156 {
157 battery_data.push(BatteryInfo {
158 name: path
159 .file_name()
160 .map_or("Unknown", |f| f.to_str().unwrap_or("Unknown"))
161 .to_owned(),
162 carbon_path,
163 energy_path,
164 })
165 }
166 }
167 Ok(battery_data)
168}
169
170pub enum FlagType {
172 OnBattery,
173}
174
175impl FlagType {
176 pub(crate) fn to_path(&self) -> &str {
177 match self {
178 FlagType::OnBattery => constants::FLAG_ONBATTERY_PATH,
179 }
180 }
181}
182
183pub fn try_load_flag_sync(flag: FlagType) -> Result<bool, MetricError> {
185 let path = flag.to_path();
186 Ok(Flag::try_read_from_fs_sync(path)?.get_value())
187}
188
189pub async fn try_load_flag(flag: FlagType) -> Result<bool, MetricError> {
191 let path = flag.to_path();
192 Ok(Flag::try_read_from_fs(path).await?.get_value())
193}