Skip to content

Commit 53898ef

Browse files
bugy855jamaal
andauthored
feat: add error mapping functionality for BTMinerV3 (#417)
Co-authored-by: jamaal <jamaal@sustainhash.com>
1 parent 5a804ca commit 53898ef

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

pyasic/miners/backends/btminer.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,10 @@ async def upgrade_firmware(
855855
str(DataOptions.FAN_PSU): DataFunction(
856856
"_get_psu_fans", [RPCAPICommand("rpc_get_device_info", "get.device.info")]
857857
),
858+
str(DataOptions.ERRORS): DataFunction(
859+
"_get_errors",
860+
[RPCAPICommand("rpc_get_device_info", "get.device.info")],
861+
),
858862
str(DataOptions.HASHBOARDS): DataFunction(
859863
"_get_hashboards",
860864
[
@@ -1130,6 +1134,40 @@ async def _get_psu_fans(self, rpc_get_device_info: dict | None = None) -> list[F
11301134
rpm = rpc_get_device_info.get("msg", {}).get("power", {}).get("fanspeed")
11311135
return [Fan(speed=rpm)] if rpm is not None else []
11321136

1137+
async def _get_errors(
1138+
self, rpc_get_device_info: dict | None = None
1139+
) -> list[MinerErrorData]:
1140+
if rpc_get_device_info is None:
1141+
try:
1142+
rpc_get_device_info = await self.rpc.get_device_info()
1143+
except APIError:
1144+
return []
1145+
1146+
if rpc_get_device_info is None:
1147+
return []
1148+
1149+
raw_errors = rpc_get_device_info.get("msg", {}).get("error-code", [])
1150+
if not isinstance(raw_errors, list):
1151+
return []
1152+
1153+
parsed_codes: list[int] = []
1154+
for item in raw_errors:
1155+
if isinstance(item, dict):
1156+
for key in item.keys():
1157+
if str(key).lower() == "reason":
1158+
continue
1159+
try:
1160+
parsed_codes.append(int(key))
1161+
except (TypeError, ValueError):
1162+
continue
1163+
else:
1164+
try:
1165+
parsed_codes.append(int(item))
1166+
except (TypeError, ValueError):
1167+
continue
1168+
1169+
return [WhatsminerError(error_code=code) for code in sorted(set(parsed_codes))]
1170+
11331171
async def _get_serial_number(
11341172
self, rpc_get_device_info: dict | None = None
11351173
) -> str | None:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import unittest
2+
from unittest.mock import AsyncMock
3+
4+
from pyasic.errors import APIError
5+
from pyasic.miners.backends.btminer import BTMinerV3
6+
7+
8+
class TestBTMinerV3ErrorMapping(unittest.IsolatedAsyncioTestCase):
9+
def setUp(self):
10+
self.miner = BTMinerV3(ip="10.1.10.24")
11+
12+
async def test_get_errors_maps_device_info_codes(self):
13+
rpc_get_device_info = {
14+
"msg": {
15+
"error-code": [
16+
"2010",
17+
541,
18+
{"541": "Slot 1 error reading chip id.", "reason": "hashboard"},
19+
{"5032": "Slot 2 voltage abnormal"},
20+
"not-a-code",
21+
None,
22+
]
23+
}
24+
}
25+
26+
errors = await self.miner._get_errors(rpc_get_device_info=rpc_get_device_info)
27+
codes = [error.error_code for error in errors]
28+
29+
self.assertEqual(codes, [541, 2010, 5032])
30+
31+
async def test_get_errors_ignores_non_list_error_code_payload(self):
32+
rpc_get_device_info = {"msg": {"error-code": "btminer process is down err"}}
33+
34+
errors = await self.miner._get_errors(rpc_get_device_info=rpc_get_device_info)
35+
36+
self.assertEqual(errors, [])
37+
38+
async def test_get_errors_returns_empty_on_rpc_api_error(self):
39+
self.miner.rpc = AsyncMock()
40+
self.miner.rpc.get_device_info = AsyncMock(side_effect=APIError("rpc failed"))
41+
42+
errors = await self.miner._get_errors()
43+
44+
self.assertEqual(errors, [])
45+
46+
47+
if __name__ == "__main__":
48+
unittest.main()

0 commit comments

Comments
 (0)