# ------------------------------------------------------------------------------ # Copyright 2022 Upstream Data Inc - # - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at - # - # http://www.apache.org/licenses/LICENSE-2.0 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ 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 class MiningModeNormal(MinerConfigValue): mode: str = field(init=False, default="normal") @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeNormal": return cls() def as_am_modern(self) -> dict: return {"miner-mode": "0"} def as_wm(self) -> dict: return {"mode": self.mode} @dataclass class MiningModeSleep(MinerConfigValue): mode: str = field(init=False, default="sleep") @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeSleep": return cls() def as_am_modern(self) -> dict: return {"miner-mode": "1"} def as_wm(self) -> dict: return {"mode": self.mode} @dataclass class MiningModeLPM(MinerConfigValue): mode: str = field(init=False, default="low") @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeLPM": return cls() def as_am_modern(self) -> dict: return {"miner-mode": "3"} def as_wm(self) -> dict: return {"mode": self.mode} @dataclass class MiningModeHPM(MinerConfigValue): mode: str = field(init=False, default="high") @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeHPM": return cls() def as_am_modern(self): return {"miner-mode": "0"} def as_wm(self) -> dict: return {"mode": self.mode} @dataclass class MiningModePowerTune(MinerConfigValue): mode: str = field(init=False, default="power_tuning") power: int = None @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModePowerTune": return cls(dict_conf.get("power")) def as_am_modern(self) -> dict: return {"miner-mode": "0"} def as_wm(self) -> dict: if self.power is not None: return {"mode": self.mode, self.mode: {"wattage": self.power}} return {} 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): mode: str = field(init=False, default="hashrate_tuning") hashrate: int = None @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeHashrateTune": return cls(dict_conf.get("hashrate")) 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): freq: float volt: float @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "ManualBoardSettings": return cls(freq=dict_conf["freq"], volt=dict_conf["volt"]) def as_am_modern(self) -> dict: return {"miner-mode": "0"} @dataclass class MiningModeManual(MinerConfigValue): mode: str = field(init=False, default="manual") global_freq: float global_volt: float boards: Dict[int, ManualBoardSettings] = field(default_factory=dict) @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual": return cls( global_freq=dict_conf["global_freq"], global_volt=dict_conf["global_volt"], boards={i: ManualBoardSettings.from_dict(dict_conf[i]) for i in dict_conf}, ) def as_am_modern(self) -> dict: return {"miner-mode": "0"} @classmethod def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual": # will raise KeyError if it cant find the settings, values cannot be empty voltage = web_overclock_settings["globals"]["volt"] freq = web_overclock_settings["globals"]["freq"] boards = { idx: ManualBoardSettings( freq=board["freq"], volt=voltage if not board["freq"] == 0 else 0, ) for idx, board in enumerate(web_overclock_settings["chains"]) } return cls(global_freq=freq, global_volt=voltage, boards=boards) class MiningModeConfig(MinerConfigOption): normal = MiningModeNormal low = MiningModeLPM high = MiningModeHPM sleep = MiningModeSleep power_tuning = MiningModePowerTune hashrate_tuning = MiningModeHashrateTune manual = MiningModeManual @classmethod def default(cls): return cls.normal() @classmethod def from_dict(cls, dict_conf: Union[dict, None]): if dict_conf is None: return cls.default() mode = dict_conf.get("mode") if mode is None: return cls.default() clsattr = getattr(cls, mode) if clsattr is not None: return clsattr().from_dict(dict_conf) @classmethod def from_am_modern(cls, web_conf: dict): if web_conf.get("bitmain-work-mode") is not None: work_mode = web_conf["bitmain-work-mode"] if work_mode == "": return cls.default() if int(work_mode) == 0: return cls.normal() elif int(work_mode) == 1: return cls.sleep() elif int(work_mode) == 3: return cls.low() return cls.default() @classmethod def from_epic(cls, web_conf: dict): try: work_mode = web_conf["PerpetualTune"]["Running"] if work_mode: if ( web_conf["PerpetualTune"]["Algorithm"].get("VoltageOptimizer") is not None ): return cls.hashrate_tuning( web_conf["PerpetualTune"]["Algorithm"]["VoltageOptimizer"][ "Target" ] ) else: return cls.hashrate_tuning( web_conf["PerpetualTune"]["Algorithm"]["ChipTune"]["Target"] ) else: return cls.normal() except KeyError: return cls.default() @classmethod def from_bosminer(cls, toml_conf: dict): if toml_conf.get("autotuning") is None: return cls.default() autotuning_conf = toml_conf["autotuning"] if autotuning_conf.get("enabled") is None: return cls.default() if not autotuning_conf["enabled"]: return cls.default() if autotuning_conf.get("psu_power_limit") is not None: # old autotuning conf return cls.power_tuning(autotuning_conf["psu_power_limit"]) if autotuning_conf.get("mode") is not None: # new autotuning conf mode = autotuning_conf["mode"] if mode == "power_target": if autotuning_conf.get("power_target") is not None: return cls.power_tuning(autotuning_conf["power_target"]) return cls.power_tuning() if mode == "hashrate_target": if autotuning_conf.get("hashrate_target") is not None: return cls.hashrate_tuning(autotuning_conf["hashrate_target"]) return cls.hashrate_tuning() @classmethod def from_vnish(cls, web_settings: dict): try: mode_settings = web_settings["miner"]["overclock"] except KeyError: return cls.default() if mode_settings["preset"] == "disabled": return MiningModeManual.from_vnish(mode_settings) else: return cls.power_tuning(int(mode_settings["preset"])) @classmethod def from_boser(cls, grpc_miner_conf: dict): try: tuner_conf = grpc_miner_conf["tuner"] if not tuner_conf.get("enabled", False): return cls.default() except LookupError: return cls.default() if tuner_conf.get("tunerMode") is not None: if tuner_conf["tunerMode"] == 1: if tuner_conf.get("powerTarget") is not None: return cls.power_tuning(tuner_conf["powerTarget"]["watt"]) return cls.power_tuning() if tuner_conf["tunerMode"] == 2: if tuner_conf.get("hashrateTarget") is not None: return cls.hashrate_tuning( int(tuner_conf["hashrateTarget"]["terahashPerSecond"]) ) return cls.hashrate_tuning() if tuner_conf.get("powerTarget") is not None: return cls.power_tuning(tuner_conf["powerTarget"]["watt"]) if tuner_conf.get("hashrateTarget") is not None: return cls.hashrate_tuning( int(tuner_conf["hashrateTarget"]["terahashPerSecond"]) )