diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 944bfbe3..12986043 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from dataclasses import asdict, dataclass +from dataclasses import dataclass from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig @@ -30,6 +30,48 @@ class MinerConfig: temperature: TemperatureConfig = TemperatureConfig.default() power_scaling: PowerScalingConfig = PowerScalingConfig.default() + def as_am_modern(self, user_suffix: str = None): + return { + **self.fan_mode.as_am_modern(), + "freq-level": "100", + **self.mining_mode.as_am_modern(), + **self.pools.as_am_modern(user_suffix=user_suffix), + **self.temperature.as_am_modern(), + **self.power_scaling.as_am_modern(), + } + + def as_wm(self, user_suffix: str = None): + return { + **self.fan_mode.as_wm(), + **self.mining_mode.as_wm(), + **self.pools.as_wm(user_suffix=user_suffix), + **self.temperature.as_wm(), + **self.power_scaling.as_wm(), + } + + def as_am_old(self, user_suffix: str = None): + return { + **self.fan_mode.as_am_old(), + **self.mining_mode.as_am_old(), + **self.pools.as_am_old(user_suffix=user_suffix), + **self.temperature.as_am_old(), + **self.power_scaling.as_am_old(), + } + if __name__ == "__main__": - print(asdict(MinerConfig())) + config = MinerConfig( + pools=PoolConfig.simple( + [ + { + "url": "stratum+tcp://stratum.test.io:3333", + "user": "user.test", + "password": "123", + } + ] + ), + mining_mode=MiningModeConfig.power_tuning(3000), + ) + print(config.as_wm()) + print(config.as_am_modern()) + print(config.as_am_old()) diff --git a/pyasic/config/base.py b/pyasic/config/base.py new file mode 100644 index 00000000..2a39477a --- /dev/null +++ b/pyasic/config/base.py @@ -0,0 +1,65 @@ +# ------------------------------------------------------------------------------ +# 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 enum import Enum + + +class MinerConfigOption(Enum): + def as_am_modern(self) -> dict: + return self.value.as_am_modern() + + def as_am_old(self) -> dict: + return self.value.as_am_old() + + def as_wm(self) -> dict: + return self.value.as_wm() + + def as_inno(self) -> dict: + return self.value.as_inno() + + def as_goldshell(self) -> dict: + return self.value.as_goldshell() + + def as_avalon(self) -> dict: + return self.value.as_avalon() + + def as_bos(self) -> dict: + return self.value.as_bos() + + def __call__(self, *args, **kwargs): + return self.value(*args, **kwargs) + + +class MinerConfigValue: + def as_am_modern(self) -> dict: + return {} + + def as_am_old(self) -> dict: + return {} + + def as_wm(self) -> dict: + return {} + + def as_inno(self) -> dict: + return {} + + def as_goldshell(self) -> dict: + return {} + + def as_avalon(self) -> dict: + return {} + + def as_bos(self) -> dict: + return {} diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index ac74b0a4..c26fbc30 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -14,24 +14,23 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field -from enum import Enum + +from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass -class FanModeNormal: +class FanModeNormal(MinerConfigValue): mode: str = field(init=False, default="auto") - @staticmethod - def as_am_modern(): + def as_am_modern(self): return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} - @staticmethod - def as_bos(): + def as_bos(self): return {"temp_control": {"mode": "auto"}} @dataclass -class FanModeManual: +class FanModeManual(MinerConfigValue): mode: str = field(init=False, default="manual") minimum_fans: int = 1 speed: int = 100 @@ -47,19 +46,17 @@ class FanModeManual: @dataclass -class FanModeImmersion: +class FanModeImmersion(MinerConfigValue): mode: str = field(init=False, default="immersion") - @staticmethod - def as_am_modern(): + def as_am_modern(self): return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"} - @staticmethod - def as_bos(): + def as_bos(self): return {"temp_control": {"mode": "manual"}, "fan_control": {"min_fans": 0}} -class FanModeConfig(Enum): +class FanModeConfig(MinerConfigOption): normal = FanModeNormal manual = FanModeManual immersion = FanModeImmersion @@ -67,6 +64,3 @@ class FanModeConfig(Enum): @classmethod def default(cls): return cls.normal() - - def __call__(self, *args, **kwargs): - return self.value(*args, **kwargs) diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index 6d779a2e..41ffb7f2 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -14,69 +14,97 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field -from enum import Enum + +from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass -class MiningModeNormal: +class MiningModeNormal(MinerConfigValue): mode: str = field(init=False, default="normal") - @staticmethod - def as_am_modern(): + def as_am_modern(self): + return {"miner-mode": "0"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModeSleep(MinerConfigValue): + mode: str = field(init=False, default="sleep") + + def as_am_modern(self): + return {"miner-mode": "1"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModeLPM(MinerConfigValue): + mode: str = field(init=False, default="low") + + def as_am_modern(self): + return {"miner-mode": "3"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModeHPM(MinerConfigValue): + mode: str = field(init=False, default="high") + + def as_am_modern(self): + return {"miner-mode": "0"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModePowerTune(MinerConfigValue): + mode: str = field(init=False, default="power_tuning") + power: int + + def as_am_modern(self): + return {"miner-mode": "0"} + + def as_wm(self): + return {"mode": self.mode, self.mode: {"wattage": self.power}} + + +@dataclass +class MiningModeHashrateTune(MinerConfigValue): + mode: str = field(init=False, default="hashrate_tuning") + hashrate: int + + def as_am_modern(self): return {"miner-mode": "0"} @dataclass -class MiningModeSleep: - mode: str = field(init=False, default="sleep") - - @staticmethod - def as_am_modern(): - return {"miner-mode": "1"} - - -@dataclass -class MiningModeLPM: - mode: str = field(init=False, default="low") - - @staticmethod - def as_am_modern(): - return {"miner-mode": "3"} - - -@dataclass -class MiningModeHPM(MiningModeNormal): - mode: str = field(init=False, default="high") - - -@dataclass -class MiningModePowerTune(MiningModeNormal): - mode: str = field(init=False, default="power_tuning") - power: int - - -@dataclass -class MiningModeHashrateTune(MiningModeNormal): - mode: str = field(init=False, default="hashrate_tuning") - hashrate: int - - -@dataclass -class ManualBoardSettings: +class ManualBoardSettings(MinerConfigValue): freq: float volt: float + def as_am_modern(self): + return {"miner-mode": "0"} + @dataclass -class MiningModeManual(MiningModeNormal): +class MiningModeManual(MinerConfigValue): mode: str = field(init=False, default="manual") global_freq: float global_volt: float boards: dict[int, ManualBoardSettings] = field(default_factory=dict) + def as_am_modern(self): + return {"miner-mode": "0"} -class MiningModeConfig(Enum): + +class MiningModeConfig(MinerConfigOption): normal = MiningModeNormal low = MiningModeLPM high = MiningModeHPM @@ -88,6 +116,3 @@ class MiningModeConfig(Enum): @classmethod def default(cls): return cls.normal() - - def __call__(self, *args, **kwargs): - return self.value(*args, **kwargs) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index d78635d4..466dba5d 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -18,39 +18,129 @@ import string from dataclasses import dataclass, field from typing import Union +from pyasic.config.base import MinerConfigValue + @dataclass -class Pool: +class Pool(MinerConfigValue): url: str user: str password: str + def as_am_modern(self, user_suffix: str = None): + if user_suffix is not None: + return { + "url": self.url, + "user": f"{self.user}{user_suffix}", + "pass": self.password, + } + return {"url": self.url, "user": self.user, "pass": self.password} + + def as_wm(self, idx: int, user_suffix: str = None): + if user_suffix is not None: + return { + f"pool_{idx}": self.url, + f"worker_{idx}": f"{self.user}{user_suffix}", + f"passwd_{idx}": self.password, + } + return { + f"pool_{idx}": self.url, + f"worker_{idx}": self.user, + f"passwd_{idx}": self.password, + } + + def as_am_old(self, idx: int, user_suffix: str = None): + if user_suffix is not None: + return { + f"_ant_pool{idx}url": self.url, + f"_ant_pool{idx}user": f"{self.user}{user_suffix}", + f"_ant_pool{idx}pw": self.password, + } + return { + f"_ant_pool{idx}url": self.url, + f"_ant_pool{idx}user": self.user, + f"_ant_pool{idx}pw": self.password, + } + @dataclass -class PoolGroup: +class PoolGroup(MinerConfigValue): pools: list[Pool] = field(default_factory=list) quota: int = 1 name: str = None def __post_init__(self): - if self.group_name is None: - self.group_name = "".join( + if self.name is None: + self.name = "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(6) ) # generate random pool group name in case it isn't set + def as_am_modern(self, user_suffix: str = None): + pools = [] + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix)) + else: + pools.append(Pool("", "", "").as_am_modern()) + idx += 1 + return pools + + def as_wm(self, user_suffix: str = None): + pools = {} + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.update( + **self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix) + ) + else: + pools.update(**Pool("", "", "").as_wm(idx=idx + 1)) + idx += 1 + return pools + + def as_am_old(self, user_suffix: str = None): + pools = {} + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.update( + **self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix) + ) + else: + pools.update(**Pool("", "", "").as_am_old(idx=idx + 1)) + idx += 1 + return pools + @dataclass -class PoolConfig: +class PoolConfig(MinerConfigValue): groups: list[PoolGroup] = field(default_factory=list) @classmethod def default(cls): return cls() - def simple(self, pools: list[Union[Pool, dict[str, str]]]): + @classmethod + def simple(cls, pools: list[Union[Pool, dict[str, str]]]): group_pools = [] for pool in pools: if isinstance(pool, dict): pool = Pool(**pool) group_pools.append(pool) - self.groups = [PoolGroup(pools=group_pools)] + return cls(groups=[PoolGroup(pools=group_pools)]) + + def as_am_modern(self, user_suffix: str = None): + if len(self.groups) > 0: + return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_am_modern()} + + def as_wm(self, user_suffix: str = None): + if len(self.groups) > 0: + return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_wm()} + + def as_am_old(self, user_suffix: str = None): + if len(self.groups) > 0: + return self.groups[0].as_am_old(user_suffix=user_suffix) + return PoolGroup().as_am_old() diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index 5e3682a0..e2bf2ca3 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -14,27 +14,28 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field -from enum import Enum + +from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass -class PowerScalingShutdownEnabled: +class PowerScalingShutdownEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") duration: int = None @dataclass -class PowerScalingShutdownDisabled: +class PowerScalingShutdownDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") -class PowerScalingShutdown(Enum): +class PowerScalingShutdown(MinerConfigOption): enabled = PowerScalingShutdownEnabled disabled = PowerScalingShutdownDisabled @dataclass -class PowerScalingEnabled: +class PowerScalingEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") power_step: int = None minimum_power: int = None @@ -42,17 +43,14 @@ class PowerScalingEnabled: @dataclass -class PowerScalingDisabled: +class PowerScalingDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") -class PowerScalingConfig(Enum): +class PowerScalingConfig(MinerConfigOption): enabled = PowerScalingEnabled disabled = PowerScalingDisabled @classmethod def default(cls): return cls.disabled() - - def __call__(self, *args, **kwargs): - return self.value(*args, **kwargs) diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py index 68645ee8..0e2b416a 100644 --- a/pyasic/config/temperature.py +++ b/pyasic/config/temperature.py @@ -15,9 +15,11 @@ # ------------------------------------------------------------------------------ from dataclasses import dataclass +from pyasic.config.base import MinerConfigValue + @dataclass -class TemperatureConfig: +class TemperatureConfig(MinerConfigValue): target: int = None hot: int = None danger: int = None