Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions asic-rs-core/src/data/capabilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! Tuning capability metadata reported by a miner's firmware.
//!
//! Where [`MinerData::tuning_target`](super::miner::MinerData::tuning_target)
//! describes the *current* tuning setpoint, these types describe the *envelope*
//! the firmware allows: the factory default plus the accepted range (for power
//! and hashrate tuning) or the set of selectable presets. Every field is
//! optional — a backend fills in only what its firmware actually exposes.

#[cfg(feature = "python")]
use pyo3::pyclass;
use serde::{Deserialize, Serialize};

use super::miner::TuningTarget;

/// Factory tuning envelope for power-target tuning.
#[cfg_attr(feature = "python", pyclass(from_py_object, module = "asic_rs"))]
#[cfg_attr(feature = "python", asic_rs_pydantic::py_pydantic_model(getters))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct PowerTuningCapabilities {
/// The factory default power target, i.e. the value tuning starts from.
pub default: Option<TuningTarget>,
/// The lowest power target the miner accepts.
pub minimum: Option<TuningTarget>,
/// The highest power target the miner accepts.
pub maximum: Option<TuningTarget>,
}

/// Factory tuning envelope for hashrate-target tuning.
#[cfg_attr(feature = "python", pyclass(from_py_object, module = "asic_rs"))]
#[cfg_attr(feature = "python", asic_rs_pydantic::py_pydantic_model(getters))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct HashRateTuningCapabilities {
/// The factory default hashrate target, i.e. the value tuning starts from.
pub default: Option<TuningTarget>,
/// The lowest hashrate target the miner accepts.
pub minimum: Option<TuningTarget>,
/// The highest hashrate target the miner accepts.
pub maximum: Option<TuningTarget>,
}

/// Factory tuning envelope for preset / mining-mode tuning.
#[cfg_attr(feature = "python", pyclass(from_py_object, module = "asic_rs"))]
#[cfg_attr(feature = "python", asic_rs_pydantic::py_pydantic_model(getters))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct PresetTuningCapabilities {
/// The factory default preset, when one is marked as default.
pub default: Option<TuningTarget>,
/// The presets the firmware offers to select between.
pub presets: Vec<TuningTarget>,
}

/// The tuning envelopes a miner exposes, grouped by tuning domain.
///
/// A backend populates only the domains its firmware supports; the rest stay
/// `None`. This keeps a single, extensible home for "what can this miner be
/// tuned to" instead of a flat list of per-aspect fields.
#[cfg_attr(feature = "python", pyclass(from_py_object, module = "asic_rs"))]
#[cfg_attr(feature = "python", asic_rs_pydantic::py_pydantic_model(getters))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct TuningCapabilities {
/// Power-target tuning envelope, when the firmware tunes by power.
pub power: Option<PowerTuningCapabilities>,
/// Hashrate-target tuning envelope, when the firmware tunes by hashrate.
pub hashrate: Option<HashRateTuningCapabilities>,
/// Preset / mining-mode tuning envelope, when the firmware tunes by preset.
pub presets: Option<PresetTuningCapabilities>,
}
4 changes: 4 additions & 0 deletions asic-rs-core/src/data/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ pub enum DataField {
/// Configured tuning target (power or hashrate).
#[cfg_attr(feature = "python", pydantic(value = "TuningTarget"))]
TuningTarget,
/// Factory tuning envelope the firmware exposes (default/min/max power or
/// hashrate, or selectable presets).
#[cfg_attr(feature = "python", pydantic(value = "TuningCapabilities"))]
TuningCapabilities,
/// Efficiency of the miner (e.g., J/TH).
#[cfg_attr(feature = "python", pydantic(value = "Efficiency"))]
Efficiency,
Expand Down
4 changes: 4 additions & 0 deletions asic-rs-core/src/data/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};

use super::{
board::{BoardData, MinerControlBoard},
capabilities::TuningCapabilities,
device::DeviceInfo,
fan::FanData,
hashrate::HashRate,
Expand Down Expand Up @@ -118,6 +119,9 @@ pub struct MinerData {
pub tuning_target: Option<TuningTarget>,
/// The current tuning target adjusted by scaling settings, when available.
pub scaled_tuning_target: Option<TuningTarget>,
/// The factory tuning envelope the firmware exposes (default / min / max
/// power and hashrate, or selectable presets), when reported.
pub tuning_capabilities: Option<TuningCapabilities>,
/// The current efficiency in W/TH/s (J/TH) of the miner
pub efficiency: Option<f64>,
/// The state of the fault/alert light on the miner
Expand Down
1 change: 1 addition & 0 deletions asic-rs-core/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! The most important data type is [`MinerData`][`miner::MinerData`], it contains all the data asic-rs gathers with `get_data`.

pub mod board;
pub mod capabilities;
pub mod collector;
pub mod command;
pub(crate) mod deserialize;
Expand Down
24 changes: 24 additions & 0 deletions asic-rs-core/src/traits/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::{
},
data::{
board::{BoardData, MinerControlBoard},
capabilities::TuningCapabilities,
collector::{DataCollector, DataField, DataLocation},
command::MinerCommand,
device::DeviceInfo,
Expand Down Expand Up @@ -130,6 +131,7 @@ pub trait GetMinerData:
+ GetWattage
+ GetTuningTarget
+ GetScaledTuningTarget
+ GetTuningCapabilities
+ GetLightFlashing
+ GetMessages
+ GetUptime
Expand Down Expand Up @@ -188,6 +190,7 @@ impl<
+ GetWattage
+ GetTuningTarget
+ GetScaledTuningTarget
+ GetTuningCapabilities
+ GetLightFlashing
+ GetMessages
+ GetUptime
Expand Down Expand Up @@ -224,6 +227,7 @@ impl<
let wattage = self.parse_wattage(&data);
let tuning_target = self.parse_tuning_target(&data);
let scaled_tuning_target = self.parse_scaled_tuning_target(&data);
let tuning_capabilities = self.parse_tuning_capabilities(&data);
let fluid_temperature = self.parse_fluid_temperature(&data);
let outlet_fluid_temperature = self.parse_outlet_fluid_temperature(&data);
let fans = self.parse_fans(&data);
Expand Down Expand Up @@ -314,6 +318,7 @@ impl<
wattage,
tuning_target,
scaled_tuning_target,
tuning_capabilities,
efficiency,

// Status information
Expand Down Expand Up @@ -651,6 +656,25 @@ pub trait GetScaledTuningTarget: CollectData {
}
}

// Tuning Capabilities
#[async_trait]
pub trait GetTuningCapabilities: CollectData {
#[tracing::instrument(level = "debug")]
async fn get_tuning_capabilities(&self) -> Option<TuningCapabilities> {
let mut collector = self.get_collector();
let data = collector.collect(&[DataField::TuningCapabilities]).await;
self.parse_tuning_capabilities(&data)
}

#[allow(unused_variables)]
fn parse_tuning_capabilities(
&self,
data: &HashMap<DataField, Value>,
) -> Option<TuningCapabilities> {
None
}
}

// Light Flashing
#[async_trait]
pub trait GetLightFlashing: CollectData {
Expand Down
1 change: 1 addition & 0 deletions asic-rs-firmwares/antminer/src/backends/v2020/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,7 @@ impl GetFluidTemperature for AntMinerV2020 {
}

impl GetPsuFans for AntMinerV2020 {}
impl GetTuningCapabilities for AntMinerV2020 {}

impl GetMessages for AntMinerV2020 {
fn parse_messages(&self, data: &HashMap<DataField, Value>) -> Vec<MinerMessage> {
Expand Down
1 change: 1 addition & 0 deletions asic-rs-firmwares/antminer/src/backends/v2023_07/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ impl GetFluidTemperature for AntMinerV202307 {
}

impl GetPsuFans for AntMinerV202307 {}
impl GetTuningCapabilities for AntMinerV202307 {}

impl GetMessages for AntMinerV202307 {
fn parse_messages(&self, data: &HashMap<DataField, Value>) -> Vec<MinerMessage> {
Expand Down
2 changes: 1 addition & 1 deletion asic-rs-firmwares/auradine/src/backends/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ impl GetTuningTarget for AuradineV1 {
}

impl GetScaledTuningTarget for AuradineV1 {}

impl GetTuningCapabilities for AuradineV1 {}
impl GetLightFlashing for AuradineV1 {
fn parse_light_flashing(&self, data: &HashMap<DataField, Value>) -> Option<bool> {
data.extract_map::<u64, _>(DataField::LightFlashing, |code| code == 3)
Expand Down
2 changes: 1 addition & 1 deletion asic-rs-firmwares/avalonminer/src/backends/avalon_a/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ impl GetTuningTarget for AvalonAMiner {
}

impl GetScaledTuningTarget for AvalonAMiner {}

impl GetTuningCapabilities for AvalonAMiner {}
impl GetLightFlashing for AvalonAMiner {
fn parse_light_flashing(&self, data: &HashMap<DataField, Value>) -> Option<bool> {
data.extract::<bool>(DataField::LightFlashing)
Expand Down
2 changes: 1 addition & 1 deletion asic-rs-firmwares/avalonminer/src/backends/avalon_q/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ impl GetTuningTarget for AvalonQMiner {
}

impl GetScaledTuningTarget for AvalonQMiner {}

impl GetTuningCapabilities for AvalonQMiner {}
impl GetLightFlashing for AvalonQMiner {
fn parse_light_flashing(&self, data: &HashMap<DataField, Value>) -> Option<bool> {
data.extract::<bool>(DataField::LightFlashing)
Expand Down
1 change: 1 addition & 0 deletions asic-rs-firmwares/bitaxe/src/backends/v2_0_0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ impl GetTuningTarget for Bitaxe200 {
impl GetScaledTuningTarget for Bitaxe200 {
// N/A
}
impl GetTuningCapabilities for Bitaxe200 {}
impl GetLightFlashing for Bitaxe200 {
// N/A
}
Expand Down
1 change: 1 addition & 0 deletions asic-rs-firmwares/bitaxe/src/backends/v2_9_0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ impl GetTuningTarget for Bitaxe290 {
impl GetScaledTuningTarget for Bitaxe290 {
// N/A
}
impl GetTuningCapabilities for Bitaxe290 {}
impl GetLightFlashing for Bitaxe290 {
// N/A
}
Expand Down
69 changes: 69 additions & 0 deletions asic-rs-firmwares/braiins/src/backends/util.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,77 @@
use asic_rs_core::data::capabilities::{
HashRateTuningCapabilities, PowerTuningCapabilities, TuningCapabilities,
};
use asic_rs_core::data::hashrate::{HashRate, HashRateUnit};
use asic_rs_core::data::miner::TuningTarget;
use measurements::Power;
use serde_json::Value;

/// Build [`TuningCapabilities`] from a BOS GraphQL `powerTarget` metadata object
/// (`{ default, min, max }`, all in watts). Older GraphQL BraiinsOS backends only
/// expose the power-target envelope this way.
pub(crate) fn power_target_capabilities(power_target: &Value) -> TuningCapabilities {
let watts = |key: &str| {
power_target
.get(key)
.and_then(|v| v.as_i64())
.map(|w| TuningTarget::from_watts(w as f64))
};
TuningCapabilities {
power: Some(PowerTuningCapabilities {
default: watts("default"),
minimum: watts("min"),
maximum: watts("max"),
}),
..Default::default()
}
}

/// Build [`TuningCapabilities`] from a BOS+ REST `tuner_constraints` object
/// (`power_target` as `PowerConstraints` in watts, `hashrate_target` as
/// `HashrateConstraints` in TH/s). Used by the REST BraiinsOS backends
/// (25.07 and newer).
pub(crate) fn tuner_constraints_capabilities(tuner: &Value) -> TuningCapabilities {
let power = tuner.get("power_target").map(|target| {
let watts = |key: &str| {
target
.pointer(&format!("/{key}/watt"))
.and_then(|v| v.as_i64())
.map(|w| TuningTarget::from_watts(w as f64))
};
PowerTuningCapabilities {
default: watts("default"),
minimum: watts("min"),
maximum: watts("max"),
}
});

let hashrate = tuner.get("hashrate_target").map(|target| {
let ths = |key: &str| {
target
.pointer(&format!("/{key}/terahash_per_second"))
.and_then(|v| v.as_f64())
.map(|value| {
TuningTarget::HashRate(HashRate {
value,
unit: HashRateUnit::TeraHash,
algo: "SHA256".to_string(),
})
})
};
HashRateTuningCapabilities {
default: ths("default"),
minimum: ths("min"),
maximum: ths("max"),
}
});

TuningCapabilities {
power,
hashrate,
presets: None,
}
}

pub(crate) fn parse_configured_tuning_target(value: &Value) -> Option<TuningTarget> {
parse_tagged_tuning_target(value, "configured")
}
Expand Down
37 changes: 36 additions & 1 deletion asic-rs-firmwares/braiins/src/backends/v21_09/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use asic_rs_core::{
},
data::{
board::BoardData,
capabilities::TuningCapabilities,
collector::{
DataCollector, DataExtensions, DataExtractor, DataField, DataLocation, get_by_pointer,
},
Expand All @@ -27,7 +28,9 @@ use measurements::{AngularVelocity, Frequency, Power, Temperature, Voltage};
use serde_json::{Value, json};
use web::BraiinsWebAPI;

use crate::backends::util::{parse_configured_tuning_target, parse_scaled_tuning_target};
use crate::backends::util::{
parse_configured_tuning_target, parse_scaled_tuning_target, power_target_capabilities,
};

use crate::{
backends::v21_09::{graphql::BraiinsGraphQLAPI, rpc::BraiinsRPCAPI},
Expand Down Expand Up @@ -196,6 +199,21 @@ impl GetDataLocations for BraiinsV2109 {
command: "admin/network/iface_status/lan",
parameters: None,
};
const GQL_POWER_TARGET_META_QUERY: MinerCommand = MinerCommand::GraphQL {
command: r#"{
bosminer {
metadata {
autotuning {
powerTarget {
default
min
max
}
}
}
}
}"#,
};

match data_field {
DataField::ApiVersion => vec![(
Expand Down Expand Up @@ -344,6 +362,14 @@ impl GetDataLocations for BraiinsV2109 {
tag: None,
},
)],
DataField::TuningCapabilities => vec![(
GQL_POWER_TARGET_META_QUERY,
DataExtractor {
func: get_by_pointer,
key: Some("/bosminer/metadata/autotuning/powerTarget"),
tag: None,
},
)],
_ => vec![],
}
}
Expand Down Expand Up @@ -394,6 +420,15 @@ impl GetFirmwareVersion for BraiinsV2109 {
}

impl GetControlBoardVersion for BraiinsV2109 {}
impl GetTuningCapabilities for BraiinsV2109 {
fn parse_tuning_capabilities(
&self,
data: &HashMap<DataField, Value>,
) -> Option<TuningCapabilities> {
let power_target = data.get(&DataField::TuningCapabilities)?;
Some(power_target_capabilities(power_target))
}
}

impl GetHashboards for BraiinsV2109 {
fn parse_hashboards(&self, data: &HashMap<DataField, Value>) -> Vec<BoardData> {
Expand Down
Loading