From aa2dc5a53d6c1e9d0ef122492d31048e692304a9 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Fri, 12 Jan 2024 15:06:44 -0700 Subject: [PATCH] feature: update some gRPC functions, and add `as_boser` for some of the `MinerConfig` values. --- pyasic/config/mining.py | 40 ++++++++++++++++ pyasic/config/power_scaling.py | 40 ++++++++-------- pyasic/miners/backends/braiins_os.py | 20 ++++---- pyasic/web/braiins_os/grpc.py | 69 +++++++++++++++++++++++++--- 4 files changed, 133 insertions(+), 36 deletions(-) diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index f4ae4781..b4101a51 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -17,6 +17,16 @@ from dataclasses import dataclass, field from typing import Dict, Union from pyasic.config.base import MinerConfigOption, MinerConfigValue +from pyasic.web.braiins_os.proto.braiins.bos.v1 import ( + HashrateTargetMode, + PerformanceMode, + Power, + PowerTargetMode, + SaveAction, + SetPerformanceModeRequest, + TeraHashrate, + TunerPerformanceMode, +) @dataclass @@ -99,6 +109,20 @@ class MiningModePowerTune(MinerConfigValue): def as_bosminer(self) -> dict: return {"autotuning": {"enabled": True, "psu_power_limit": self.power}} + def as_boser(self) -> dict: + return { + "set_performance_mode": SetPerformanceModeRequest( + save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY, + mode=PerformanceMode( + tuner_mode=TunerPerformanceMode( + power_target=PowerTargetMode( + power_target=Power(watt=self.power) + ) + ) + ), + ), + } + @dataclass class MiningModeHashrateTune(MinerConfigValue): @@ -112,6 +136,22 @@ class MiningModeHashrateTune(MinerConfigValue): def as_am_modern(self) -> dict: return {"miner-mode": "0"} + def as_boser(self) -> dict: + return { + "set_performance_mode": SetPerformanceModeRequest( + save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY, + mode=PerformanceMode( + tuner_mode=TunerPerformanceMode( + hashrate_target=HashrateTargetMode( + hashrate_target=TeraHashrate( + terahash_per_second=self.hashrate + ) + ) + ) + ), + ) + } + @dataclass class ManualBoardSettings(MinerConfigValue): diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index 0e4bf336..3c64ec26 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -17,7 +17,13 @@ from dataclasses import dataclass, field from typing import Union from pyasic.config.base import MinerConfigOption, MinerConfigValue -from pyasic.web.braiins_os.proto.braiins.bos.v1 import DpsPowerTarget, DpsTarget, Hours +from pyasic.web.braiins_os.proto.braiins.bos.v1 import ( + DpsPowerTarget, + DpsTarget, + Hours, + Power, + SetDpsRequest, +) @dataclass @@ -38,12 +44,7 @@ class PowerScalingShutdownEnabled(MinerConfigValue): return cfg def as_boser(self) -> dict: - cfg = {"enable_shutdown ": True} - - if self.duration is not None: - cfg["shutdown_duration"] = Hours(self.duration) - - return cfg + return {"enable_shutdown": True, "shutdown_duration": self.duration} @dataclass @@ -147,19 +148,18 @@ class PowerScalingEnabled(MinerConfigValue): return {"power_scaling": cfg} def as_boser(self) -> dict: - cfg = {"enable": True} - target_conf = {} - if self.power_step is not None: - target_conf["power_step"] = self.power_step - if self.minimum_power is not None: - target_conf["min_power_target"] = self.minimum_power - - cfg["target"] = DpsTarget(power_target=DpsPowerTarget(**target_conf)) - - if self.shutdown_enabled is not None: - cfg = {**cfg, **self.shutdown_enabled.as_boser()} - - return {"dps": cfg} + return { + "set_dps": SetDpsRequest( + enable=True, + **self.shutdown_enabled.as_boser(), + target=DpsTarget( + power_target=DpsPowerTarget( + power_step=Power(self.power_step), + min_power_target=Power(self.minimum_power), + ) + ), + ), + } @dataclass diff --git a/pyasic/miners/backends/braiins_os.py b/pyasic/miners/backends/braiins_os.py index e8850922..49a9bf98 100644 --- a/pyasic/miners/backends/braiins_os.py +++ b/pyasic/miners/backends/braiins_os.py @@ -792,22 +792,22 @@ class BOSer(BaseMiner): return MinerConfig.from_boser(grpc_conf) async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: + raise NotImplementedError logging.debug(f"{self}: Sending config.") self.config = config - raise NotImplementedError async def set_power_limit(self, wattage: int) -> bool: try: - cfg = await self.get_config() - if cfg is None: - return False - cfg.mining_mode = MiningModePowerTune(wattage) - await self.send_config(cfg) - except Exception as e: - logging.warning(f"{self} set_power_limit: {e}") + result = await self.web.grpc.set_power_target(wattage) + except APIError: return False - else: - return True + + try: + if result["powerTarget"]["watt"] == wattage: + return True + except KeyError: + pass + return False ################################################## ### DATA GATHERING FUNCTIONS (get_{some_data}) ### diff --git a/pyasic/web/braiins_os/grpc.py b/pyasic/web/braiins_os/grpc.py index 0f63a887..8fa7e240 100644 --- a/pyasic/web/braiins_os/grpc.py +++ b/pyasic/web/braiins_os/grpc.py @@ -14,6 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ import asyncio +import logging from datetime import timedelta from betterproto import Message @@ -284,16 +285,72 @@ class BOSerGRPCAPI: async def set_dps( self, + enable: bool, + power_step: int, + min_power_target: int, + enable_shutdown: bool = None, + shutdown_duration: int = None, ): - raise NotImplementedError - return await self.send_command("braiins.bos.v1.PerformanceService/SetDPS") - - async def set_performance_mode(self): - raise NotImplementedError return await self.send_command( - "braiins.bos.v1.PerformanceService/SetPerformanceMode" + "set_dps", + message=SetDpsRequest( + enable=enable, + enable_shutdown=enable_shutdown, + shutdown_duration=shutdown_duration, + target=DpsTarget( + power_target=DpsPowerTarget( + power_step=Power(power_step), + min_power_target=Power(min_power_target), + ) + ), + ), ) + async def set_performance_mode( + self, + wattage_target: int = None, + hashrate_target: int = None, + save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, + ): + if wattage_target is not None and hashrate_target is not None: + logging.error( + "Cannot use both wattage_target and hashrate_target, using wattage_target." + ) + elif wattage_target is None and hashrate_target is None: + raise APIError( + "No target supplied, please supply either wattage_target or hashrate_target." + ) + if wattage_target is not None: + return await self.send_command( + "set_performance_mode", + message=SetPerformanceModeRequest( + save_action=save_action, + mode=PerformanceMode( + tuner_mode=TunerPerformanceMode( + power_target=PowerTargetMode( + power_target=Power(watt=wattage_target) + ) + ) + ), + ), + ) + if hashrate_target is not None: + return await self.send_command( + "set_performance_mode", + message=SetPerformanceModeRequest( + save_action=save_action, + mode=PerformanceMode( + tuner_mode=TunerPerformanceMode( + hashrate_target=HashrateTargetMode( + hashrate_target=TeraHashrate( + terahash_per_second=hashrate_target + ) + ) + ) + ), + ), + ) + async def get_active_performance_mode(self): return await self.send_command( "get_active_performance_mode", GetPerformanceModeRequest()