added actual miners versions and types to the factory
This commit is contained in:
@@ -514,6 +514,14 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
"""
|
"""
|
||||||
API 'get_version' command.
|
API 'get_version' command.
|
||||||
|
|
||||||
|
Returns a dict containing version information.
|
||||||
|
"""
|
||||||
|
return await self.get_version()
|
||||||
|
|
||||||
|
async def get_version(self):
|
||||||
|
"""
|
||||||
|
API 'get_version' command.
|
||||||
|
|
||||||
Returns a dict containing version information.
|
Returns a dict containing version information.
|
||||||
"""
|
"""
|
||||||
return await self.send_command("get_version")
|
return await self.send_command("get_version")
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ async def scan_network(network):
|
|||||||
async for found_miner in get_miner_genenerator:
|
async for found_miner in get_miner_genenerator:
|
||||||
all_miners.append(found_miner)
|
all_miners.append(found_miner)
|
||||||
all_miners.sort(key=lambda x: x.ip)
|
all_miners.sort(key=lambda x: x.ip)
|
||||||
window["ip_table"].update([[str(miner.ip), "", "", "", ""] for miner in all_miners])
|
window["ip_table"].update([[str(miner.ip)] for miner in all_miners])
|
||||||
progress_bar_len += 1
|
progress_bar_len += 1
|
||||||
asyncio.create_task(update_prog_bar(progress_bar_len))
|
asyncio.create_task(update_prog_bar(progress_bar_len))
|
||||||
await update_ui_with_data("ip_count", str(len(all_miners)))
|
await update_ui_with_data("ip_count", str(len(all_miners)))
|
||||||
@@ -147,7 +147,7 @@ async def scan_and_get_data(network):
|
|||||||
# can output "Identifying" for each found item, but it gets a bit cluttered
|
# can output "Identifying" for each found item, but it gets a bit cluttered
|
||||||
# and could possibly be confusing for the end user because of timing on
|
# and could possibly be confusing for the end user because of timing on
|
||||||
# adding the IPs
|
# adding the IPs
|
||||||
# window["ip_table"].update([["Identifying...", "", "", "", ""] for miner in miners])
|
# window["ip_table"].update([["Identifying..."] for miner in miners])
|
||||||
progress_bar_len += 1
|
progress_bar_len += 1
|
||||||
asyncio.create_task(update_prog_bar(progress_bar_len))
|
asyncio.create_task(update_prog_bar(progress_bar_len))
|
||||||
progress_bar_len += network_size - len(miners)
|
progress_bar_len += network_size - len(miners)
|
||||||
@@ -157,7 +157,7 @@ async def scan_and_get_data(network):
|
|||||||
async for found_miner in get_miner_genenerator:
|
async for found_miner in get_miner_genenerator:
|
||||||
all_miners.append(found_miner)
|
all_miners.append(found_miner)
|
||||||
all_miners.sort(key=lambda x: x.ip)
|
all_miners.sort(key=lambda x: x.ip)
|
||||||
window["ip_table"].update([[str(miner.ip), "", "", "", ""] for miner in all_miners])
|
window["ip_table"].update([[str(miner.ip)] for miner in all_miners])
|
||||||
progress_bar_len += 1
|
progress_bar_len += 1
|
||||||
asyncio.create_task(update_prog_bar(progress_bar_len))
|
asyncio.create_task(update_prog_bar(progress_bar_len))
|
||||||
await update_ui_with_data("ip_count", str(len(all_miners)))
|
await update_ui_with_data("ip_count", str(len(all_miners)))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import toml
|
|||||||
from config.bos import bos_config_convert, general_config_convert_bos
|
from config.bos import bos_config_convert, general_config_convert_bos
|
||||||
|
|
||||||
|
|
||||||
class BOSminerX17(BaseMiner):
|
class BOSMinerX17(BaseMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str) -> None:
|
||||||
api = BOSMinerAPI(ip)
|
api = BOSMinerAPI(ip)
|
||||||
super().__init__(ip, api)
|
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.bosminer import BOSminer
|
||||||
from miners.bmminer import BMMiner
|
from miners.bmminer import BMMiner
|
||||||
from miners.cgminer import CGMiner
|
from miners.cgminer import CGMiner
|
||||||
@@ -32,37 +41,53 @@ class MinerFactory:
|
|||||||
for miner in scanned:
|
for miner in scanned:
|
||||||
yield await miner
|
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."""
|
"""Decide a miner type using the IP address of the miner."""
|
||||||
# check if the miner already exists in cache
|
# check if the miner already exists in cache
|
||||||
if ip in self.miners:
|
if ip in self.miners:
|
||||||
return self.miners[ip]
|
return self.miners[ip]
|
||||||
# get the version data
|
miner = UnknownMiner(str(ip))
|
||||||
version = None
|
api = None
|
||||||
for i in range(GET_VERSION_RETRIES):
|
for i in range(GET_VERSION_RETRIES):
|
||||||
version_data = await self._get_version_data(ip)
|
api = await self._get_api_type(ip)
|
||||||
if version_data:
|
if api:
|
||||||
# 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())
|
|
||||||
break
|
break
|
||||||
if version:
|
model = None
|
||||||
# check version against different return miner types
|
for i in range(GET_VERSION_RETRIES):
|
||||||
if "BOSminer" in version or "BOSminer+" in version:
|
model = await self._get_miner_model(ip)
|
||||||
miner = BOSminer(str(ip))
|
if model:
|
||||||
elif "CGMiner" in version:
|
break
|
||||||
miner = CGMiner(str(ip))
|
if model:
|
||||||
elif "BMMiner" in version:
|
if "Antminer" in model:
|
||||||
miner = BMMiner(str(ip))
|
if model == "Antminer S9":
|
||||||
elif "BTMiner" in version:
|
if "BOSMiner" in api:
|
||||||
miner = BTMiner(str(ip))
|
miner = BOSMinerS9(str(ip))
|
||||||
else:
|
elif "CGMiner" in api:
|
||||||
print(f"Bad API response: {version}")
|
miner = CGMiner(str(ip))
|
||||||
miner = UnknownMiner(str(ip))
|
elif "BMMiner" in api:
|
||||||
else:
|
miner = BMMiner(str(ip))
|
||||||
# if we don't get version, miner type is unknown
|
elif "17" in model:
|
||||||
print(f"No API response: {str(ip)}")
|
if "BOSMiner" in api:
|
||||||
miner = UnknownMiner(str(ip))
|
miner = BOSMinerX17(str(ip))
|
||||||
# save the miner in cache
|
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
|
self.miners[ip] = miner
|
||||||
return miner
|
return miner
|
||||||
|
|
||||||
@@ -70,101 +95,107 @@ class MinerFactory:
|
|||||||
"""Clear the miner factory cache."""
|
"""Clear the miner factory cache."""
|
||||||
self.miners = {}
|
self.miners = {}
|
||||||
|
|
||||||
@staticmethod
|
async def _get_miner_model(self, ip: ipaddress.ip_address or str) -> dict or None:
|
||||||
async def _get_version_data(ip: ipaddress.ip_address) -> dict or None:
|
model = None
|
||||||
"""Get data on the version of the miner to return the right miner."""
|
try:
|
||||||
for i in range(3):
|
data = await self._send_api_command(str(ip), "devdetails")
|
||||||
try:
|
if data.get("STATUS"):
|
||||||
# open a connection to the miner
|
if data["STATUS"][0].get("STATUS") not in ["I", "S"]:
|
||||||
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
|
|
||||||
try:
|
try:
|
||||||
reader, writer = await asyncio.wait_for(fut, timeout=7)
|
data = await self._send_api_command(str(ip), "version")
|
||||||
except asyncio.exceptions.TimeoutError:
|
model = data["VERSION"][0]["Type"]
|
||||||
return None
|
except:
|
||||||
|
print(f"Get Model Exception: {ip}")
|
||||||
# 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"])
|
|
||||||
else:
|
else:
|
||||||
# check for stupid whatsminer formatting
|
model = data["DEVDETAILS"][0]["Model"]
|
||||||
if not isinstance(data["STATUS"], list):
|
if model:
|
||||||
if data["STATUS"] not in ("S", "I"):
|
return model
|
||||||
raise APIError(data["Msg"])
|
except OSError as e:
|
||||||
else:
|
if e.winerror == 121:
|
||||||
if "whatsminer" in data["Description"]:
|
return None
|
||||||
return {"VERSION": [{"BTMiner": data["Description"]}]}
|
else:
|
||||||
# make sure the command succeeded
|
print(ip, e)
|
||||||
elif data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
return None
|
||||||
# this is an error
|
|
||||||
raise APIError(data["STATUS"][0]["Msg"])
|
async def _send_api_command(self, ip: ipaddress.ip_address or str, command: str):
|
||||||
# return the data
|
try:
|
||||||
return data
|
# get reader and writer streams
|
||||||
except OSError as e:
|
reader, writer = await asyncio.open_connection(str(ip), 4028)
|
||||||
if e.winerror == 121:
|
# handle OSError 121
|
||||||
return None
|
except OSError as e:
|
||||||
else:
|
if e.winerror == "121":
|
||||||
print(ip, e)
|
print("Semaphore Timeout has Expired.")
|
||||||
# except json.decoder.JSONDecodeError:
|
return {}
|
||||||
# print("Decode Error @ " + str(ip) + str(data))
|
|
||||||
# except Exception as e:
|
# create the command
|
||||||
# print(ip, e)
|
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
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user