added actual miners versions and types to the factory
This commit is contained in:
@@ -5,7 +5,7 @@ import toml
|
||||
from config.bos import bos_config_convert, general_config_convert_bos
|
||||
|
||||
|
||||
class BOSminerX17(BaseMiner):
|
||||
class BOSMinerX17(BaseMiner):
|
||||
def __init__(self, ip: str) -> None:
|
||||
api = BOSMinerAPI(ip)
|
||||
super().__init__(ip, api)
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
from miners.antminer.S9.bos import BOSMinerS9
|
||||
from miners.antminer.X17.bos import BOSMinerX17
|
||||
|
||||
from miners.whatsminer.M20 import BTMinerM20
|
||||
from miners.whatsminer.M21 import BTMinerM21
|
||||
from miners.whatsminer.M30 import BTMinerM30
|
||||
from miners.whatsminer.M31 import BTMinerM31
|
||||
from miners.whatsminer.M32 import BTMinerM32
|
||||
|
||||
from miners.bosminer import BOSminer
|
||||
from miners.bmminer import BMMiner
|
||||
from miners.cgminer import CGMiner
|
||||
@@ -32,37 +41,53 @@ class MinerFactory:
|
||||
for miner in scanned:
|
||||
yield await miner
|
||||
|
||||
async def get_miner(self, ip: ipaddress.ip_address) -> BOSminer or CGMiner or BMMiner or UnknownMiner:
|
||||
async def get_miner(self, ip: ipaddress.ip_address) -> BOSminer or CGMiner or BMMiner or BTMiner or UnknownMiner:
|
||||
"""Decide a miner type using the IP address of the miner."""
|
||||
# check if the miner already exists in cache
|
||||
if ip in self.miners:
|
||||
return self.miners[ip]
|
||||
# get the version data
|
||||
version = None
|
||||
miner = UnknownMiner(str(ip))
|
||||
api = None
|
||||
for i in range(GET_VERSION_RETRIES):
|
||||
version_data = await self._get_version_data(ip)
|
||||
if version_data:
|
||||
# if we got version data, get a list of the keys so we can check type of miner
|
||||
version = list(version_data['VERSION'][0].keys())
|
||||
api = await self._get_api_type(ip)
|
||||
if api:
|
||||
break
|
||||
if version:
|
||||
# check version against different return miner types
|
||||
if "BOSminer" in version or "BOSminer+" in version:
|
||||
miner = BOSminer(str(ip))
|
||||
elif "CGMiner" in version:
|
||||
miner = CGMiner(str(ip))
|
||||
elif "BMMiner" in version:
|
||||
miner = BMMiner(str(ip))
|
||||
elif "BTMiner" in version:
|
||||
miner = BTMiner(str(ip))
|
||||
else:
|
||||
print(f"Bad API response: {version}")
|
||||
miner = UnknownMiner(str(ip))
|
||||
else:
|
||||
# if we don't get version, miner type is unknown
|
||||
print(f"No API response: {str(ip)}")
|
||||
miner = UnknownMiner(str(ip))
|
||||
# save the miner in cache
|
||||
model = None
|
||||
for i in range(GET_VERSION_RETRIES):
|
||||
model = await self._get_miner_model(ip)
|
||||
if model:
|
||||
break
|
||||
if model:
|
||||
if "Antminer" in model:
|
||||
if model == "Antminer S9":
|
||||
if "BOSMiner" in api:
|
||||
miner = BOSMinerS9(str(ip))
|
||||
elif "CGMiner" in api:
|
||||
miner = CGMiner(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMiner(str(ip))
|
||||
elif "17" in model:
|
||||
if "BOSMiner" in api:
|
||||
miner = BOSMinerX17(str(ip))
|
||||
elif "CGMiner" in api:
|
||||
miner = CGMiner(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMiner(str(ip))
|
||||
elif "19" in model:
|
||||
if "CGMiner" in api:
|
||||
miner = CGMiner(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMiner(str(ip))
|
||||
elif "M20" in model:
|
||||
miner = BTMinerM20(str(ip))
|
||||
elif "M21" in model:
|
||||
miner = BTMinerM21(str(ip))
|
||||
elif "M30" in model:
|
||||
miner = BTMinerM30(str(ip))
|
||||
elif "M31" in model:
|
||||
miner = BTMinerM31(str(ip))
|
||||
elif "M32" in model:
|
||||
miner = BTMinerM32(str(ip))
|
||||
self.miners[ip] = miner
|
||||
return miner
|
||||
|
||||
@@ -70,101 +95,107 @@ class MinerFactory:
|
||||
"""Clear the miner factory cache."""
|
||||
self.miners = {}
|
||||
|
||||
@staticmethod
|
||||
async def _get_version_data(ip: ipaddress.ip_address) -> dict or None:
|
||||
"""Get data on the version of the miner to return the right miner."""
|
||||
for i in range(3):
|
||||
try:
|
||||
# open a connection to the miner
|
||||
fut = asyncio.open_connection(str(ip), 4028)
|
||||
# get reader and writer streams
|
||||
try:
|
||||
reader, writer = await asyncio.wait_for(fut, timeout=7)
|
||||
except asyncio.exceptions.TimeoutError:
|
||||
return None
|
||||
|
||||
# create the command
|
||||
cmd = {"command": "version"}
|
||||
|
||||
# send the command
|
||||
writer.write(json.dumps(cmd).encode('utf-8'))
|
||||
await writer.drain()
|
||||
|
||||
# instantiate data
|
||||
data = b""
|
||||
|
||||
# loop to receive all the data
|
||||
while True:
|
||||
d = await reader.read(4096)
|
||||
if not d:
|
||||
break
|
||||
data += d
|
||||
|
||||
if data.endswith(b"\x00"):
|
||||
data = json.loads(data.decode('utf-8')[:-1])
|
||||
else:
|
||||
# some stupid whatsminers need a different command
|
||||
fut = asyncio.open_connection(str(ip), 4028)
|
||||
# get reader and writer streams
|
||||
async def _get_miner_model(self, ip: ipaddress.ip_address or str) -> dict or None:
|
||||
model = None
|
||||
try:
|
||||
data = await self._send_api_command(str(ip), "devdetails")
|
||||
if data.get("STATUS"):
|
||||
if data["STATUS"][0].get("STATUS") not in ["I", "S"]:
|
||||
try:
|
||||
reader, writer = await asyncio.wait_for(fut, timeout=7)
|
||||
except asyncio.exceptions.TimeoutError:
|
||||
return None
|
||||
|
||||
# create the command
|
||||
cmd = {"command": "get_version"}
|
||||
|
||||
# send the command
|
||||
writer.write(json.dumps(cmd).encode('utf-8'))
|
||||
await writer.drain()
|
||||
|
||||
# instantiate data
|
||||
data = b""
|
||||
|
||||
# loop to receive all the data
|
||||
while True:
|
||||
d = await reader.read(4096)
|
||||
if not d:
|
||||
break
|
||||
data += d
|
||||
|
||||
data = data.decode('utf-8').replace("\n", "")
|
||||
data = json.loads(data)
|
||||
|
||||
# close the connection
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
# check if the data returned is correct or an error
|
||||
# if status isn't a key, it is a multicommand
|
||||
if "STATUS" not in data.keys():
|
||||
for key in data.keys():
|
||||
# make sure not to try to turn id into a dict
|
||||
if not key == "id":
|
||||
# make sure they succeeded
|
||||
if data[key][0]["STATUS"][0]["STATUS"] not in ["S", "I"]:
|
||||
# this is an error
|
||||
raise APIError(data["STATUS"][0]["Msg"])
|
||||
data = await self._send_api_command(str(ip), "version")
|
||||
model = data["VERSION"][0]["Type"]
|
||||
except:
|
||||
print(f"Get Model Exception: {ip}")
|
||||
else:
|
||||
# check for stupid whatsminer formatting
|
||||
if not isinstance(data["STATUS"], list):
|
||||
if data["STATUS"] not in ("S", "I"):
|
||||
raise APIError(data["Msg"])
|
||||
else:
|
||||
if "whatsminer" in data["Description"]:
|
||||
return {"VERSION": [{"BTMiner": data["Description"]}]}
|
||||
# make sure the command succeeded
|
||||
elif data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
||||
# this is an error
|
||||
raise APIError(data["STATUS"][0]["Msg"])
|
||||
# return the data
|
||||
return data
|
||||
except OSError as e:
|
||||
if e.winerror == 121:
|
||||
return None
|
||||
else:
|
||||
print(ip, e)
|
||||
# except json.decoder.JSONDecodeError:
|
||||
# print("Decode Error @ " + str(ip) + str(data))
|
||||
# except Exception as e:
|
||||
# print(ip, e)
|
||||
model = data["DEVDETAILS"][0]["Model"]
|
||||
if model:
|
||||
return model
|
||||
except OSError as e:
|
||||
if e.winerror == 121:
|
||||
return None
|
||||
else:
|
||||
print(ip, e)
|
||||
return None
|
||||
|
||||
async def _send_api_command(self, ip: ipaddress.ip_address or str, command: str):
|
||||
try:
|
||||
# get reader and writer streams
|
||||
reader, writer = await asyncio.open_connection(str(ip), 4028)
|
||||
# handle OSError 121
|
||||
except OSError as e:
|
||||
if e.winerror == "121":
|
||||
print("Semaphore Timeout has Expired.")
|
||||
return {}
|
||||
|
||||
# create the command
|
||||
cmd = {"command": command}
|
||||
|
||||
# send the command
|
||||
writer.write(json.dumps(cmd).encode('utf-8'))
|
||||
await writer.drain()
|
||||
|
||||
# instantiate data
|
||||
data = b""
|
||||
|
||||
# loop to receive all the data
|
||||
try:
|
||||
while True:
|
||||
d = await reader.read(4096)
|
||||
if not d:
|
||||
break
|
||||
data += d
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
try:
|
||||
# some json from the API returns with a null byte (\x00) on the end
|
||||
if data.endswith(b"\x00"):
|
||||
# handle the null byte
|
||||
str_data = data.decode('utf-8')[:-1]
|
||||
else:
|
||||
# no null byte
|
||||
str_data = data.decode('utf-8')
|
||||
# fix an error with a btminer return having an extra comma that breaks json.loads()
|
||||
str_data = str_data.replace(",}", "}")
|
||||
# fix an error with a btminer return having a newline that breaks json.loads()
|
||||
str_data = str_data.replace("\n", "")
|
||||
# fix an error with a bmminer return not having a specific comma that breaks json.loads()
|
||||
str_data = str_data.replace("}{", "},{")
|
||||
# parse the json
|
||||
parsed_data = json.loads(str_data)
|
||||
# handle bad json
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print(e)
|
||||
raise APIError(f"Decode Error: {data}")
|
||||
data = parsed_data
|
||||
|
||||
# close the connection
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
async def _get_api_type(self, ip: ipaddress.ip_address or str) -> dict or None:
|
||||
"""Get data on the version of the miner to return the right miner."""
|
||||
api = None
|
||||
try:
|
||||
data = await self._send_api_command(str(ip), "version")
|
||||
if data.get("STATUS") and not data.get("STATUS") == "E":
|
||||
if data["STATUS"][0].get("STATUS") in ["I", "S"]:
|
||||
if "BMMiner" in data["VERSION"][0].keys():
|
||||
api = "BMMiner"
|
||||
elif "CGMiner" in data["VERSION"][0].keys():
|
||||
api = "CGMiner"
|
||||
elif "BOSminer" in data["VERSION"][0].keys() or "BOSminer+" in data["VERSION"][0].keys():
|
||||
api = "BOSMiner"
|
||||
elif data.get("Description") and "whatsminer" in data.get("Description"):
|
||||
api = "BTMiner"
|
||||
if api:
|
||||
return api
|
||||
except OSError as e:
|
||||
if e.winerror == 121:
|
||||
return None
|
||||
else:
|
||||
print(ip, e)
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user