feature: optimize multicommand on new X19 models.

This commit is contained in:
UpstreamData
2023-07-24 11:34:16 -06:00
parent 2653db90e3
commit 03f2a1f9ba
8 changed files with 161 additions and 96 deletions

View File

@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
import logging import logging
from pyasic.API import APIError, BaseMinerAPI from pyasic.API import APIError, BaseMinerAPI
@@ -56,19 +56,19 @@ class BFGMinerAPI(BaseMinerAPI):
return data return data
async def _x19_multicommand(self, *commands) -> dict: async def _x19_multicommand(self, *commands) -> dict:
data = None tasks = []
try: # send all commands individually
data = {} for cmd in commands:
# send all commands individually tasks.append(
for cmd in commands: asyncio.create_task(self._handle_multicommand(cmd, allow_warning=True))
data[cmd] = []
data[cmd].append(await self.send_command(cmd, allow_warning=True))
except APIError:
pass
except Exception as e:
logging.warning(
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
) )
all_data = await asyncio.gather(*tasks)
data = {}
for item in all_data:
data.update(item)
return data return data
async def version(self) -> dict: async def version(self) -> dict:

View File

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
import logging import logging
from pyasic.API import APIError, BaseMinerAPI from pyasic.API import APIError, BaseMinerAPI
@@ -57,21 +58,19 @@ class BMMinerAPI(BaseMinerAPI):
return data return data
async def _x19_multicommand(self, *commands, allow_warning: bool = True) -> dict: async def _x19_multicommand(self, *commands, allow_warning: bool = True) -> dict:
data = None tasks = []
try: # send all commands individually
data = {} for cmd in commands:
# send all commands individually tasks.append(
for cmd in commands: asyncio.create_task(self._handle_multicommand(cmd, allow_warning=True))
data[cmd] = []
data[cmd].append(
await self.send_command(cmd, allow_warning=allow_warning)
)
except APIError:
pass
except Exception as e:
logging.warning(
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
) )
all_data = await asyncio.gather(*tasks)
data = {}
for item in all_data:
data.update(item)
return data return data
async def version(self) -> dict: async def version(self) -> dict:

View File

@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
import logging import logging
from pyasic.API import APIError, BaseMinerAPI from pyasic.API import APIError, BaseMinerAPI
@@ -56,19 +56,19 @@ class CGMinerAPI(BaseMinerAPI):
return data return data
async def _x19_multicommand(self, *commands) -> dict: async def _x19_multicommand(self, *commands) -> dict:
data = None tasks = []
try: # send all commands individually
data = {} for cmd in commands:
# send all commands individually tasks.append(
for cmd in commands: asyncio.create_task(self._handle_multicommand(cmd, allow_warning=True))
data[cmd] = []
data[cmd].append(await self.send_command(cmd, allow_warning=True))
except APIError:
pass
except Exception as e:
logging.warning(
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
) )
all_data = await asyncio.gather(*tasks)
data = {}
for item in all_data:
data.update(item)
return data return data
async def version(self) -> dict: async def version(self) -> dict:

View File

@@ -20,7 +20,7 @@ import logging
import time import time
from dataclasses import asdict, dataclass, field, fields from dataclasses import asdict, dataclass, field, fields
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import List, Union, Any from typing import Any, List, Union
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
@@ -411,8 +411,12 @@ class MinerData:
field_data.append(f'error_{idx+1}="{item.error_message}"') field_data.append(f'error_{idx+1}="{item.error_message}"')
elif attribute == "hashboards": elif attribute == "hashboards":
for idx, item in enumerate(self[attribute]): for idx, item in enumerate(self[attribute]):
field_data.append(f"hashboard_{idx+1}_hashrate={item.get('hashrate', 0.0)}") field_data.append(
field_data.append(f"hashboard_{idx+1}_temperature={item.get('temp', 0)}") f"hashboard_{idx+1}_hashrate={item.get('hashrate', 0.0)}"
)
field_data.append(
f"hashboard_{idx+1}_temperature={item.get('temp', 0)}"
)
field_data.append( field_data.append(
f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}" f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}"
) )

View File

@@ -26,11 +26,17 @@ from pyasic.miners.backends.cgminer import CGMiner
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
ANTMINER_MODERN_DATA_LOC = { ANTMINER_MODERN_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}}, "mac": {
"cmd": "get_mac",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"model": {"cmd": "get_model", "kwargs": {}}, "model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, "api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, "fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}}, "hostname": {
"cmd": "get_hostname",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, "hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": { "nominal_hashrate": {
"cmd": "get_nominal_hashrate", "cmd": "get_nominal_hashrate",
@@ -42,8 +48,11 @@ ANTMINER_MODERN_DATA_LOC = {
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, "wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, "fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, "fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}}, "errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}}, "fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, "pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": { "is_mining": {
"cmd": "is_mining", "cmd": "is_mining",
@@ -121,21 +130,31 @@ class AntminerModern(BMMiner):
await self.send_config(cfg) await self.send_config(cfg)
return True return True
async def get_hostname(self) -> Union[str, None]: async def get_hostname(self, web_get_system_info: dict = None) -> Union[str, None]:
try: if not web_get_system_info:
data = await self.web.get_system_info() try:
if data: web_get_system_info = await self.web.get_system_info()
return data["hostname"] except APIError:
except KeyError: pass
pass
async def get_mac(self) -> Union[str, None]: if web_get_system_info:
try: try:
data = await self.web.get_system_info() return web_get_system_info["hostname"]
if data: except KeyError:
return data["macaddr"] pass
except KeyError:
pass async def get_mac(self, web_get_system_info: dict = None) -> Union[str, None]:
if not web_get_system_info:
try:
web_get_system_info = await self.web.get_system_info()
except APIError:
pass
if web_get_system_info:
try:
return web_get_system_info["macaddr"]
except KeyError:
pass
try: try:
data = await self.web.get_network_info() data = await self.web.get_network_info()
@@ -144,12 +163,17 @@ class AntminerModern(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_errors(self) -> List[MinerErrorData]: async def get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
errors = [] if not web_summary:
data = await self.web.summary()
if data:
try: try:
for item in data["SUMMARY"][0]["status"]: web_summary = await self.web.summary()
except APIError:
pass
errors = []
if web_summary:
try:
for item in web_summary["SUMMARY"][0]["status"]:
try: try:
if not item["status"] == "s": if not item["status"] == "s":
errors.append(X19Error(item["msg"])) errors.append(X19Error(item["msg"]))
@@ -159,15 +183,21 @@ class AntminerModern(BMMiner):
pass pass
return errors return errors
async def get_fault_light(self) -> bool: async def get_fault_light(self, web_get_blink_status: dict = None) -> bool:
if self.light: if self.light:
return self.light return self.light
try:
data = await self.web.get_blink_status() if not web_get_blink_status:
if data: try:
self.light = data["blink"] web_get_blink_status = await self.web.get_blink_status()
except KeyError: except APIError:
pass pass
if web_get_blink_status:
try:
self.light = web_get_blink_status["blink"]
except KeyError:
pass
return self.light return self.light
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]: async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:

View File

@@ -1078,7 +1078,9 @@ class BOSMiner(BaseMiner):
async def is_mining(self, api_devdetails: dict = None) -> Optional[bool]: async def is_mining(self, api_devdetails: dict = None) -> Optional[bool]:
if not api_devdetails: if not api_devdetails:
try: try:
api_devdetails = await self.api.send_command("devdetails", ignore_errors=True, allow_warning=False) api_devdetails = await self.api.send_command(
"devdetails", ignore_errors=True, allow_warning=False
)
except APIError: except APIError:
pass pass

View File

@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
import ipaddress import ipaddress
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
@@ -430,17 +430,34 @@ class BaseMiner(ABC):
continue continue
if len(api_multicommand) > 0: if len(api_multicommand) > 0:
api_command_data = await self.api.multicommand( api_command_task = asyncio.create_task(
*api_multicommand, allow_warning=allow_warning self.api.multicommand(*api_multicommand, allow_warning=allow_warning)
) )
else: else:
api_command_data = {} api_command_task = asyncio.sleep(0)
if len(web_multicommand) > 0: if len(web_multicommand) > 0:
web_command_data = await self.web.multicommand( web_command_task = asyncio.create_task(
*web_multicommand, allow_warning=allow_warning self.web.multicommand(*web_multicommand, allow_warning=allow_warning)
) )
else: else:
web_command_task = asyncio.sleep(0)
import pprint
from datetime import datetime
s1 = datetime.now()
web_command_data = await web_command_task
if web_command_data is None:
web_command_data = {} web_command_data = {}
print("WEB", datetime.now() - s1)
# pprint.pprint(web_command_data)
s2 = datetime.now()
api_command_data = await api_command_task
if api_command_data is None:
api_command_data = {}
print("API:", datetime.now() - s2)
# pprint.pprint(api_command_data)
miner_data = {} miner_data = {}

View File

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
import json import json
from typing import Union from typing import Union
@@ -56,25 +57,37 @@ class AntminerModernWebAPI(BaseWebAPI):
async def multicommand( async def multicommand(
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
) -> dict: ) -> dict:
data = {k: None for k in commands}
data["multicommand"] = True
auth = httpx.DigestAuth(self.username, self.pwd)
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
for command in commands: tasks = [
try: asyncio.create_task(self._handle_multicommand(client, command))
url = f"http://{self.ip}/cgi-bin/{command}.cgi" for command in commands
ret = await client.get(url, auth=auth) ]
except httpx.HTTPError: all_data = await asyncio.gather(*tasks)
pass
else: data = {}
if ret.status_code == 200: for item in all_data:
try: data.update(item)
json_data = ret.json()
data[command] = json_data data["multicommand"] = True
except json.decoder.JSONDecodeError:
pass
return data return data
async def _handle_multicommand(self, client: httpx.AsyncClient, command: str):
auth = httpx.DigestAuth(self.username, self.pwd)
try:
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
ret = await client.get(url, auth=auth)
except httpx.HTTPError:
pass
else:
if ret.status_code == 200:
try:
json_data = ret.json()
return {command: json_data}
except json.decoder.JSONDecodeError:
pass
return {command: {}}
async def get_miner_conf(self) -> dict: async def get_miner_conf(self) -> dict:
return await self.send_command("get_miner_conf") return await self.send_command("get_miner_conf")